@templatical/editor 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AiChatSidebar-DYDtzTDd.js → AiChatSidebar-ftdKHSR-.js} +15 -15
- package/dist/{AiFeatureMenu-BJ882XCi.js → AiFeatureMenu-aNeCRlil.js} +7 -7
- package/dist/{BlockIssueBadge-D6y8vQpL.js → BlockIssueBadge-bTDu6W_2.js} +5 -5
- package/dist/{BlockPreviewCanvas-DQNxLy7u.js → BlockPreviewCanvas-BsOuOfzB.js} +20 -19
- package/dist/{CloudHeaderExtras-CqG3RaKw.js → CloudHeaderExtras-BNpFohqt.js} +5 -5
- package/dist/{CloudPanels-B6xrk7kN.js → CloudPanels-xoGWwqSH.js} +7 -7
- package/dist/{CollaboratorBar-by16gXQH.js → CollaboratorBar-mEqXkdEi.js} +4 -4
- package/dist/{ColorPicker-E5gqlCZE.js → ColorPicker-FL5Vb9bE.js} +7 -7
- package/dist/{CommentsPanel-B_kzvTHR.js → CommentsPanel-slfi74ng.js} +2 -2
- package/dist/{CommentsSidebar-gfzUX7cd.js → CommentsSidebar-Dea4spMl.js} +14 -14
- package/dist/{CountdownBlock-BgNK7fZY.js → CountdownBlock-DQTwaRxP.js} +3 -3
- package/dist/{CountdownToolbar-DU8UHx9A.js → CountdownToolbar-C7xwJoqh.js} +4 -4
- package/dist/{DesignReferenceSidebar-DgndMB4H.js → DesignReferenceSidebar-BtBd7bbz.js} +11 -11
- package/dist/{FieldRow-0-UbQR4a.js → FieldRow-BWt7ECqo.js} +1 -1
- package/dist/{IssuesPanel-DmJTb_SZ.js → IssuesPanel-Ci2H36L2.js} +8 -8
- package/dist/{LoadingTrack-5KBr5cE8.js → LoadingTrack-SQXoZVf2.js} +1 -1
- package/dist/{MergeTagModeToggle-CZnJ2Gz_.js → MergeTagModeToggle-Cdi_DmVw.js} +6 -6
- package/dist/{MergeTagSuggestion-DyUcY_9e.js → MergeTagSuggestion-CoCuzEmW.js} +4 -4
- package/dist/{ParagraphEditor-CnB4kXle.js → ParagraphEditor-DQEOFc0K.js} +26 -26
- package/dist/{RestoreVersionDialog-BpOphPiE.js → RestoreVersionDialog-CHwngBae.js} +5 -5
- package/dist/{RichTextEditorContent-ChPNgHQ_.js → RichTextEditorContent-C_9xi7dD.js} +8 -8
- package/dist/{SaveBlockDialog-CKw4Va6j.js → SaveBlockDialog-tZqZzPjW.js} +21 -21
- package/dist/SavedBlocksBrowserModal-Bdj3GOD_.js +322 -0
- package/dist/{SavedBlocksPanels-ChV8bOgM.js → SavedBlocksPanels-2sBxAH9w.js} +2 -2
- package/dist/{SavedBlocksPickBar-BbEeD5RB.js → SavedBlocksPickBar-DTjQBR00.js} +4 -4
- package/dist/{TemplateScoringPanel-De8BZpRU.js → TemplateScoringPanel-B6nHSrBD.js} +15 -15
- package/dist/{TestEmailModal-4d2pXeHU.js → TestEmailModal-DuHbzO37.js} +11 -11
- package/dist/{TestEmailPanel-DEgCesNP.js → TestEmailPanel-TpwP-eK_.js} +2 -2
- package/dist/{TitleEditor-89pmKLVY.js → TitleEditor-C0JQJu38.js} +12 -12
- package/dist/{TplModal-DswccJRJ.js → TplModal-BMGmhquu.js} +5 -5
- package/dist/VersionHistoryMenu-D2x6MMYx.js +157 -0
- package/dist/{VersionHistoryPanels-CFLHDH9P.js → VersionHistoryPanels-aGlUVEGF.js} +2 -2
- package/dist/{VersionPreviewBanner-DhG5RcRt.js → VersionPreviewBanner-61t3_Z-a.js} +3 -3
- package/dist/{blockTypeIcons-dpKs5GQc.js → blockTypeIcons-BsNup009.js} +2 -2
- package/dist/bundle-stats.json +7 -7
- package/dist/{ca-DZSrLNXK.js → ca-C0hfqt4U.js} +5 -0
- package/dist/cdn/chunks/{AiChatSidebar-DmYDRmvX.js → AiChatSidebar-CqWvtRwB.js} +9 -9
- package/dist/cdn/chunks/{AiChatSidebar-DmYDRmvX.js.map → AiChatSidebar-CqWvtRwB.js.map} +1 -1
- package/dist/cdn/chunks/{AiFeatureMenu-C0Rt08qb.js → AiFeatureMenu-DoFrLcXJ.js} +6 -6
- package/dist/cdn/chunks/{AiFeatureMenu-C0Rt08qb.js.map → AiFeatureMenu-DoFrLcXJ.js.map} +1 -1
- package/dist/cdn/chunks/{BlockIssueBadge-BBog2CMy.js → BlockIssueBadge-B_mUH69a.js} +5 -5
- package/dist/cdn/chunks/{BlockIssueBadge-BBog2CMy.js.map → BlockIssueBadge-B_mUH69a.js.map} +1 -1
- package/dist/cdn/chunks/{BlockPreviewCanvas-DNBjBKBI.js → BlockPreviewCanvas-CSIxs7hr.js} +21 -20
- package/dist/cdn/chunks/BlockPreviewCanvas-CSIxs7hr.js.map +1 -0
- package/dist/cdn/chunks/{CloudHeaderExtras-Ditfv7y9.js → CloudHeaderExtras-Dgtr7WaE.js} +5 -5
- package/dist/cdn/chunks/{CloudHeaderExtras-Ditfv7y9.js.map → CloudHeaderExtras-Dgtr7WaE.js.map} +1 -1
- package/dist/cdn/chunks/{CloudPanels-C4U5bJO6.js → CloudPanels-B91lUmof.js} +7 -7
- package/dist/cdn/chunks/{CloudPanels-C4U5bJO6.js.map → CloudPanels-B91lUmof.js.map} +1 -1
- package/dist/cdn/chunks/{CollaboratorBar-B79JR9NH.js → CollaboratorBar-CQwPWoFu.js} +5 -5
- package/dist/cdn/chunks/{CollaboratorBar-B79JR9NH.js.map → CollaboratorBar-CQwPWoFu.js.map} +1 -1
- package/dist/cdn/chunks/{ColorPicker-C-ZiwUV3.js → ColorPicker-CdrKX48J.js} +7 -7
- package/dist/cdn/chunks/{ColorPicker-C-ZiwUV3.js.map → ColorPicker-CdrKX48J.js.map} +1 -1
- package/dist/cdn/chunks/{CommentsPanel-D1Ke8PbO.js → CommentsPanel-BJNgTmAA.js} +3 -3
- package/dist/cdn/chunks/{CommentsPanel-D1Ke8PbO.js.map → CommentsPanel-BJNgTmAA.js.map} +1 -1
- package/dist/cdn/chunks/{CommentsSidebar-B-UyhIZN.js → CommentsSidebar-D6VIIPhB.js} +5 -5
- package/dist/cdn/chunks/{CommentsSidebar-B-UyhIZN.js.map → CommentsSidebar-D6VIIPhB.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownBlock-CWeJBC0L.js → CountdownBlock-BRwXUvGU.js} +4 -4
- package/dist/cdn/chunks/{CountdownBlock-CWeJBC0L.js.map → CountdownBlock-BRwXUvGU.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-DyOHOu44.js → CountdownToolbar-CVaU5WG6.js} +5 -5
- package/dist/cdn/chunks/{CountdownToolbar-DyOHOu44.js.map → CountdownToolbar-CVaU5WG6.js.map} +1 -1
- package/dist/cdn/chunks/{DesignReferenceSidebar-BCCX1FtP.js → DesignReferenceSidebar-BvOwE1kg.js} +7 -7
- package/dist/cdn/chunks/{DesignReferenceSidebar-BCCX1FtP.js.map → DesignReferenceSidebar-BvOwE1kg.js.map} +1 -1
- package/dist/cdn/chunks/{FieldRow-CbcpsxZ4.js → FieldRow-D9OJhYeb.js} +2 -2
- package/dist/cdn/chunks/{FieldRow-CbcpsxZ4.js.map → FieldRow-D9OJhYeb.js.map} +1 -1
- package/dist/cdn/chunks/{IssuesPanel-3vD3ImHF.js → IssuesPanel-MJZxkScz.js} +6 -6
- package/dist/cdn/chunks/{IssuesPanel-3vD3ImHF.js.map → IssuesPanel-MJZxkScz.js.map} +1 -1
- package/dist/cdn/chunks/{LoadingTrack-Cz1sT0wP.js → LoadingTrack-Cm3TDKb3.js} +2 -2
- package/dist/cdn/chunks/{LoadingTrack-Cz1sT0wP.js.map → LoadingTrack-Cm3TDKb3.js.map} +1 -1
- package/dist/cdn/chunks/{MergeTagModeToggle-BU0SDRow.js → MergeTagModeToggle-bNuN6PTV.js} +6 -6
- package/dist/cdn/chunks/{MergeTagModeToggle-BU0SDRow.js.map → MergeTagModeToggle-bNuN6PTV.js.map} +1 -1
- package/dist/cdn/chunks/{MergeTagSuggestion-B9vL0VaG.js → MergeTagSuggestion-t8Uj00Er.js} +3 -3
- package/dist/cdn/chunks/{MergeTagSuggestion-B9vL0VaG.js.map → MergeTagSuggestion-t8Uj00Er.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-CSwIuvoF.js → ParagraphEditor-BU8tlFbL.js} +23 -23
- package/dist/cdn/chunks/{ParagraphEditor-CSwIuvoF.js.map → ParagraphEditor-BU8tlFbL.js.map} +1 -1
- package/dist/cdn/chunks/{RestoreVersionDialog-Bvg2WvJN.js → RestoreVersionDialog-vdRFfyy0.js} +5 -5
- package/dist/cdn/chunks/{RestoreVersionDialog-Bvg2WvJN.js.map → RestoreVersionDialog-vdRFfyy0.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-DdkmEEpK.js → RichTextEditorContent-C1-5I8Df.js} +7 -7
- package/dist/cdn/chunks/{RichTextEditorContent-DdkmEEpK.js.map → RichTextEditorContent-C1-5I8Df.js.map} +1 -1
- package/dist/cdn/chunks/{SaveBlockDialog-D-QO3LuR.js → SaveBlockDialog-DWaeqcM8.js} +9 -9
- package/dist/cdn/chunks/{SaveBlockDialog-D-QO3LuR.js.map → SaveBlockDialog-DWaeqcM8.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-BRdN56r7.js → SavedBlocksBrowserModal-qC7UhW74.js} +110 -111
- package/dist/cdn/chunks/SavedBlocksBrowserModal-qC7UhW74.js.map +1 -0
- package/dist/cdn/chunks/{SavedBlocksPanels-D9GTlLgb.js → SavedBlocksPanels-BvtjtdsY.js} +3 -3
- package/dist/cdn/chunks/{SavedBlocksPanels-D9GTlLgb.js.map → SavedBlocksPanels-BvtjtdsY.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksPickBar-DSRcUeIo.js → SavedBlocksPickBar-CxXjF3RN.js} +5 -5
- package/dist/cdn/chunks/{SavedBlocksPickBar-DSRcUeIo.js.map → SavedBlocksPickBar-CxXjF3RN.js.map} +1 -1
- package/dist/cdn/chunks/{TemplateScoringPanel-Dy5oOVhB.js → TemplateScoringPanel-CroWaWqy.js} +50 -50
- package/dist/cdn/chunks/{TemplateScoringPanel-Dy5oOVhB.js.map → TemplateScoringPanel-CroWaWqy.js.map} +1 -1
- package/dist/cdn/chunks/{TestEmailModal-B4L2lmIV.js → TestEmailModal-iWxAZRRq.js} +10 -10
- package/dist/cdn/chunks/{TestEmailModal-B4L2lmIV.js.map → TestEmailModal-iWxAZRRq.js.map} +1 -1
- package/dist/cdn/chunks/{TestEmailPanel-cTPgEf-D.js → TestEmailPanel-CGUtEuwp.js} +3 -3
- package/dist/cdn/chunks/{TestEmailPanel-cTPgEf-D.js.map → TestEmailPanel-CGUtEuwp.js.map} +1 -1
- package/dist/cdn/chunks/{TitleEditor-BKjL2Zs1.js → TitleEditor-DvXUW2j-.js} +12 -12
- package/dist/cdn/chunks/{TitleEditor-BKjL2Zs1.js.map → TitleEditor-DvXUW2j-.js.map} +1 -1
- package/dist/cdn/chunks/{TplModal-DxrgNmt9.js → TplModal-X-qNoLGZ.js} +5 -5
- package/dist/cdn/chunks/{TplModal-DxrgNmt9.js.map → TplModal-X-qNoLGZ.js.map} +1 -1
- package/dist/cdn/chunks/VersionHistoryMenu-yKzwxf7n.js +150 -0
- package/dist/cdn/chunks/VersionHistoryMenu-yKzwxf7n.js.map +1 -0
- package/dist/cdn/chunks/{VersionHistoryPanels-CxIFBJsz.js → VersionHistoryPanels--hQaanjH.js} +3 -3
- package/dist/cdn/chunks/{VersionHistoryPanels-CxIFBJsz.js.map → VersionHistoryPanels--hQaanjH.js.map} +1 -1
- package/dist/cdn/chunks/{VersionPreviewBanner-CdsZvVYF.js → VersionPreviewBanner-DWU_SqMd.js} +4 -4
- package/dist/cdn/chunks/{VersionPreviewBanner-CdsZvVYF.js.map → VersionPreviewBanner-DWU_SqMd.js.map} +1 -1
- package/dist/cdn/chunks/{blockTypeIcons-DXPd_hd1.js → blockTypeIcons-ZEzEpCti.js} +2 -2
- package/dist/cdn/chunks/{blockTypeIcons-DXPd_hd1.js.map → blockTypeIcons-ZEzEpCti.js.map} +1 -1
- package/dist/cdn/chunks/{ca-DZSrLNXK.js → ca-C0hfqt4U.js} +6 -1
- package/dist/cdn/chunks/ca-C0hfqt4U.js.map +1 -0
- package/dist/cdn/chunks/{cloud-CITqEHEc.js → cloud-BP5-6__d.js} +4 -4
- package/dist/cdn/chunks/{cloud-CITqEHEc.js.map → cloud-BP5-6__d.js.map} +1 -1
- package/dist/cdn/chunks/{createCloudRuntime-DwIXvrb0.js → createCloudRuntime-DD6ppb71.js} +10 -10
- package/dist/cdn/chunks/{createCloudRuntime-DwIXvrb0.js.map → createCloudRuntime-DD6ppb71.js.map} +1 -1
- package/dist/{de-DbayqpK-.js → cdn/chunks/de-C6J2Iae8.js} +7 -0
- package/dist/cdn/chunks/de-C6J2Iae8.js.map +1 -0
- package/dist/cdn/chunks/{dist-CgR3inPR.js → dist-BW6Y49zE.js} +2 -2
- package/dist/cdn/chunks/{dist-CgR3inPR.js.map → dist-BW6Y49zE.js.map} +1 -1
- package/dist/cdn/chunks/{dist-El3fQnfk.js → dist-CAFTQ6Y2.js} +5 -5
- package/dist/cdn/chunks/{dist-El3fQnfk.js.map → dist-CAFTQ6Y2.js.map} +1 -1
- package/dist/cdn/chunks/{draggable-D2vo-aQI.js → draggable-kbOhCgnk.js} +12 -13
- package/dist/cdn/chunks/{draggable-D2vo-aQI.js.map → draggable-kbOhCgnk.js.map} +1 -1
- package/dist/cdn/chunks/{en-l0ib5X0f.js → en-BaUYOQi8.js} +6 -1
- package/dist/cdn/chunks/en-BaUYOQi8.js.map +1 -0
- package/dist/cdn/chunks/en-mEKGL1PX.js +2 -0
- package/dist/cdn/chunks/{es-CMQ-5PJP.js → es-CKDYfJYn.js} +6 -1
- package/dist/cdn/chunks/es-CKDYfJYn.js.map +1 -0
- package/dist/cdn/chunks/{extensions-C87Z6_23.js → extensions-BUPHNpKD.js} +7 -7
- package/dist/cdn/chunks/{extensions-C87Z6_23.js.map → extensions-BUPHNpKD.js.map} +1 -1
- package/dist/cdn/chunks/formatAbsoluteDateTime-DMmop3C-.js +15 -0
- package/dist/cdn/chunks/formatAbsoluteDateTime-DMmop3C-.js.map +1 -0
- package/dist/{fr-DNXmZWAq.js → cdn/chunks/fr-L8DR73mK.js} +7 -0
- package/dist/cdn/chunks/fr-L8DR73mK.js.map +1 -0
- package/dist/cdn/chunks/{icons-ciivb5BS.js → icons-SP5Itd-A.js} +9 -9
- package/dist/cdn/chunks/icons-SP5Itd-A.js.map +1 -0
- package/dist/cdn/chunks/keys-BOn5i9jV.js +12 -0
- package/dist/cdn/chunks/keys-BOn5i9jV.js.map +1 -0
- package/dist/cdn/chunks/{liquid.browser-BPvKXNfO.js → liquid.browser-BypTT2oc.js} +4 -4
- package/dist/cdn/chunks/{liquid.browser-BPvKXNfO.js.map → liquid.browser-BypTT2oc.js.map} +1 -1
- package/dist/{nl-BylRRHYJ.js → cdn/chunks/nl-GDWeuX18.js} +7 -0
- package/dist/cdn/chunks/nl-GDWeuX18.js.map +1 -0
- package/dist/cdn/chunks/{preRenderCustomBlocks-DyCIgmqJ.js → preRenderCustomBlocks-LFB-e-0m.js} +2 -2
- package/dist/cdn/chunks/{preRenderCustomBlocks-DyCIgmqJ.js.map → preRenderCustomBlocks-LFB-e-0m.js.map} +1 -1
- package/dist/cdn/chunks/{pt-BR-BVoJhpie.js → pt-BR-B1AVVhWj.js} +6 -1
- package/dist/cdn/chunks/pt-BR-B1AVVhWj.js.map +1 -0
- package/dist/cdn/chunks/{src-6J-yyZoD.js → src-BM3f9I7t.js} +268 -268
- package/dist/cdn/chunks/{src-6J-yyZoD.js.map → src-BM3f9I7t.js.map} +1 -1
- package/dist/cdn/chunks/{src-BD60gqPW.js → src-CLoCCDfs.js} +26 -26
- package/dist/cdn/chunks/{src-BD60gqPW.js.map → src-CLoCCDfs.js.map} +1 -1
- package/dist/cdn/chunks/{src-Bid3cmHa.js → src-Cxl-9O-L.js} +699 -684
- package/dist/cdn/chunks/src-Cxl-9O-L.js.map +1 -0
- package/dist/cdn/chunks/{src-Cf0ohOog.js → src-DtnvR_Hl.js} +19 -19
- package/dist/cdn/chunks/src-DtnvR_Hl.js.map +1 -0
- package/dist/cdn/chunks/{tiptap-Ey0FpaX7.js → tiptap-Epe8m3Vo.js} +2636 -2629
- package/dist/cdn/chunks/tiptap-Epe8m3Vo.js.map +1 -0
- package/dist/cdn/chunks/{useAliveFlag-DrXxPV8V.js → useAliveFlag-CT9gjvO_.js} +2 -2
- package/dist/cdn/chunks/{useAliveFlag-DrXxPV8V.js.map → useAliveFlag-CT9gjvO_.js.map} +1 -1
- package/dist/cdn/chunks/{useCloudI18n-D0AoLV0a.js → useCloudI18n-DbsLlCbZ.js} +3 -3
- package/dist/cdn/chunks/{useCloudI18n-D0AoLV0a.js.map → useCloudI18n-DbsLlCbZ.js.map} +1 -1
- package/dist/cdn/chunks/{useEditorCore-CTNB2Mkl.js → useEditorCore-ChlgO2GN.js} +556 -412
- package/dist/cdn/chunks/useEditorCore-ChlgO2GN.js.map +1 -0
- package/dist/cdn/chunks/useEditorRoot-BlF1WPeu.js +10 -0
- package/dist/cdn/chunks/{useEditorRoot-BFGRBGts.js.map → useEditorRoot-BlF1WPeu.js.map} +1 -1
- package/dist/cdn/chunks/{useI18n-DIhawILp.js → useI18n-BdxNNw6j.js} +3 -3
- package/dist/cdn/chunks/{useI18n-DIhawILp.js.map → useI18n-BdxNNw6j.js.map} +1 -1
- package/dist/cdn/chunks/{useMergeTag-lWo7So18.js → useMergeTag-CI2WUNrR.js} +4 -4
- package/dist/cdn/chunks/{useMergeTag-lWo7So18.js.map → useMergeTag-CI2WUNrR.js.map} +1 -1
- package/dist/cdn/chunks/{usePopoverPosition-DkiTLnBm.js → usePopoverPosition-Dw1nTSNx.js} +3 -3
- package/dist/cdn/chunks/{usePopoverPosition-DkiTLnBm.js.map → usePopoverPosition-Dw1nTSNx.js.map} +1 -1
- package/dist/cdn/chunks/{useScrollToBlock-KcSAuXKz.js → useScrollToBlock-BMis3-PR.js} +3 -3
- package/dist/cdn/chunks/{useScrollToBlock-KcSAuXKz.js.map → useScrollToBlock-BMis3-PR.js.map} +1 -1
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +1099 -1143
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{check-80E6ZGin.js → check-JK8Uacq0.js} +1 -1
- package/dist/{chevron-down-BmkLsn2c.js → chevron-down-a-fb_Ijn.js} +1 -1
- package/dist/{chevron-up-DJSAzlUU.js → chevron-up-x4l0et_Y.js} +1 -1
- package/dist/{circle-alert-BsCfZryY.js → circle-alert-CJDW5LZo.js} +1 -1
- package/dist/{clock-BcLIngSK.js → clock-BW6rXk2f.js} +1 -1
- package/dist/{cloud-DwJ8ebNl.js → cloud-BMzRKCbk.js} +3 -3
- package/dist/{createCloudRuntime-CdXQdl34.js → createCloudRuntime-zNcMjRgb.js} +8 -8
- package/dist/{createLucideIcon-eeetEgBb.js → createLucideIcon-Bvw1Zx56.js} +3 -3
- package/dist/{cdn/chunks/de-DbayqpK-.js → de-C6J2Iae8.js} +5 -2
- package/dist/{dist-BFcAowKZ.js → dist-BxFbxpgW.js} +5 -5
- package/dist/{dist-Do83S47T.js → dist-ByT5fgKq.js} +2 -2
- package/dist/{dist-CQ9T1yi-.js → dist-C1tsW_99.js} +2 -2
- package/dist/{dist-Cd0LvUlh.js → dist-CtF9STPS.js} +2 -2
- package/dist/{dist-CvW1nNzy.js → dist-CtMXAH7a.js} +2 -2
- package/dist/dist-D08oVYhr.js +5 -0
- package/dist/{dist-prgdllHG.js → dist-D7WY8VPq.js} +2 -2
- package/dist/{dist-D8MekYFl.js → dist-DC-3T0ED.js} +2 -2
- package/dist/{dist-BC1y_U2m.js → dist-DL9334yz.js} +1418 -1411
- package/dist/{dist-BkAVGSRr.js → dist-DkcA6epL.js} +7 -7
- package/dist/dist-DzdeVn-W.js +5 -0
- package/dist/{dist-CzVIVbSa.js → dist-PL6vP4SC.js} +61 -61
- package/dist/{dist-CuuPnIMy.js → dist-dNiknO7j.js} +4 -4
- package/dist/{dist-CWC5t5J3.js → dist-f7HXo6j6.js} +2 -2
- package/dist/{dist-BUpBA58N.js → dist-heatwAdo.js} +1 -1
- package/dist/{en-l0ib5X0f.js → en-BaUYOQi8.js} +5 -0
- package/dist/{es-CMQ-5PJP.js → es-CKDYfJYn.js} +5 -0
- package/dist/{extensions-BYImwYeO.js → extensions-kOYwOh72.js} +21 -21
- package/dist/formatAbsoluteDateTime-DMmop3C-.js +13 -0
- package/dist/{cdn/chunks/fr-DNXmZWAq.js → fr-L8DR73mK.js} +5 -2
- package/dist/{image-up-BfxY85ar.js → image-up-CuzFc1mu.js} +1 -1
- package/dist/{info-LX4QFBr6.js → info-DybAwcA3.js} +1 -1
- package/dist/keys-BOhFlHsv.js +10 -0
- package/dist/{liquid.browser-CJgDDvQz.js → liquid.browser-wPG0n4Cs.js} +3 -3
- package/dist/{loader-circle-Bwima0v8.js → loader-circle-t-SVgmJ3.js} +1 -1
- package/dist/{message-circle-Box-mf2k.js → message-circle-DtwKrlhN.js} +1 -1
- package/dist/{cdn/chunks/nl-BylRRHYJ.js → nl-GDWeuX18.js} +5 -2
- package/dist/{pencil-BM9gSq_J.js → pencil-2qhC-Y4q.js} +1 -1
- package/dist/{preRenderCustomBlocks-2KyBJocY.js → preRenderCustomBlocks-CSIF-HyJ.js} +1 -1
- package/dist/{pt-BR-BVoJhpie.js → pt-BR-B1AVVhWj.js} +5 -0
- package/dist/{refresh-cw-BuzFX0wH.js → refresh-cw-C8SBuPdg.js} +1 -1
- package/dist/{runtime-dom.esm-bundler-Co_SPqAz.js → runtime-dom.esm-bundler-BuG03jBM.js} +3 -3
- package/dist/{scan-line-Duq1WXoG.js → scan-line-E7w4yQyv.js} +1 -1
- package/dist/{send-DZ4nOnIA.js → send-BoqJliF6.js} +1 -1
- package/dist/{shield-check-Cz4BRYdv.js → shield-check-D0dMaHtE.js} +1 -1
- package/dist/{sparkles-DP8RqOeL.js → sparkles-Dy2BJjiX.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/templatical-editor.js +1236 -1281
- package/dist/{text-align-start-D7tXhjUP.js → text-align-start-DrxjxvIy.js} +1 -1
- package/dist/{trash-2-BS7lZeyb.js → trash-rYMzzHZd.js} +2 -2
- package/dist/{triangle-alert-Cx4mQGjx.js → triangle-alert-DxnFj4-B.js} +1 -1
- package/dist/{upload-L8tLVM10.js → upload-DZu1adQz.js} +1 -1
- package/dist/{useAliveFlag-AFbwfHlJ.js → useAliveFlag-PVmp95MT.js} +1 -1
- package/dist/{useCloudI18n-BA69BpQ_.js → useCloudI18n-BlMajbKp.js} +2 -2
- package/dist/{useEditorCore-CtR46sbJ.js → useEditorCore-CwQYCSXO.js} +1212 -1067
- package/dist/{useEditorRoot-Cdo8Vhxa.js → useEditorRoot-C92q8uV-.js} +2 -2
- package/dist/{useI18n-B135ASaG.js → useI18n-Ce_BRzYv.js} +2 -2
- package/dist/{useMergeTag-Cx36rtrt.js → useMergeTag-CrXJ49lr.js} +3 -3
- package/dist/{usePopoverPosition-DAOHr_iM.js → usePopoverPosition-CPk7Twye.js} +2 -2
- package/dist/{useScrollToBlock-tndC8vKo.js → useScrollToBlock-DvpMfWs2.js} +3 -3
- package/dist/{vue.runtime.esm-bundler-BhOUdXMi.js → vue.runtime.esm-bundler-D9JUcKrV.js} +4 -4
- package/dist/{x-ChIQ7HwK.js → x-DJOd1hS4.js} +1 -1
- package/package.json +23 -23
- package/dist/SavedBlocksBrowserModal-SST-7EMK.js +0 -323
- package/dist/VersionHistoryMenu-DBxSJR1k.js +0 -155
- package/dist/cdn/chunks/BlockPreviewCanvas-DNBjBKBI.js.map +0 -1
- package/dist/cdn/chunks/SavedBlocksBrowserModal-BRdN56r7.js.map +0 -1
- package/dist/cdn/chunks/VersionHistoryMenu-CMqZFUHE.js +0 -148
- package/dist/cdn/chunks/VersionHistoryMenu-CMqZFUHE.js.map +0 -1
- package/dist/cdn/chunks/ca-DZSrLNXK.js.map +0 -1
- package/dist/cdn/chunks/de-DbayqpK-.js.map +0 -1
- package/dist/cdn/chunks/en-BzFt4b80.js +0 -2
- package/dist/cdn/chunks/en-l0ib5X0f.js.map +0 -1
- package/dist/cdn/chunks/es-CMQ-5PJP.js.map +0 -1
- package/dist/cdn/chunks/fr-DNXmZWAq.js.map +0 -1
- package/dist/cdn/chunks/icons-ciivb5BS.js.map +0 -1
- package/dist/cdn/chunks/keys-BJ770Mwm.js +0 -12
- package/dist/cdn/chunks/keys-BJ770Mwm.js.map +0 -1
- package/dist/cdn/chunks/nl-BylRRHYJ.js.map +0 -1
- package/dist/cdn/chunks/pt-BR-BVoJhpie.js.map +0 -1
- package/dist/cdn/chunks/src-Bid3cmHa.js.map +0 -1
- package/dist/cdn/chunks/src-Cf0ohOog.js.map +0 -1
- package/dist/cdn/chunks/tiptap-Ey0FpaX7.js.map +0 -1
- package/dist/cdn/chunks/useEditorCore-CTNB2Mkl.js.map +0 -1
- package/dist/cdn/chunks/useEditorRoot-BFGRBGts.js +0 -10
- package/dist/dist-BQqIUwvB.js +0 -5
- package/dist/dist-waDcdb1l.js +0 -5
- package/dist/keys-BJtoezGU.js +0 -10
package/dist/cdn/chunks/{SavedBlocksBrowserModal-BRdN56r7.js → SavedBlocksBrowserModal-qC7UhW74.js}
RENAMED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { B as e,
|
|
2
|
-
import "./useEditorCore-
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
5
|
-
import { K as
|
|
6
|
-
import { v as
|
|
7
|
-
import { t as
|
|
8
|
-
import { t as
|
|
9
|
-
import { t as
|
|
1
|
+
import { B as e, E as t, F as n, H as r, N as i, Q as a, R as ee, S as o, U as s, a as c, at as l, b as u, c as d, ct as f, f as p, g as m, h, k as te, l as g, lt as ne, m as _, o as v, p as y, u as b, ut as x, x as re } from "./draggable-kbOhCgnk.js";
|
|
2
|
+
import "./useEditorCore-ChlgO2GN.js";
|
|
3
|
+
import { G as S, H as ie, L as ae, h as oe } from "./keys-BOn5i9jV.js";
|
|
4
|
+
import { t as se } from "./useI18n-BdxNNw6j.js";
|
|
5
|
+
import { K as ce, N as le, Y as C, d as ue, n as de } from "./icons-SP5Itd-A.js";
|
|
6
|
+
import { v as fe } from "./styleConstants-C_YAPdQS.js";
|
|
7
|
+
import { t as w } from "./blockTypeIcons-ZEzEpCti.js";
|
|
8
|
+
import { t as pe } from "./formatAbsoluteDateTime-DMmop3C-.js";
|
|
9
|
+
import { t as me } from "./formatRelativeTime-CtUU-QZ8.js";
|
|
10
|
+
import { t as he } from "./TplModal-X-qNoLGZ.js";
|
|
10
11
|
//#region src/components/SavedBlocksBrowserModal.vue?vue&type=script&setup=true&lang.ts
|
|
11
|
-
var
|
|
12
|
+
var ge = {
|
|
12
13
|
role: "dialog",
|
|
13
14
|
"aria-modal": "true",
|
|
14
15
|
"data-testid": "saved-blocks-browser",
|
|
@@ -18,54 +19,54 @@ var me = {
|
|
|
18
19
|
"background-color": "var(--tpl-bg-elevated)",
|
|
19
20
|
"box-shadow": "var(--tpl-shadow-xl)"
|
|
20
21
|
}
|
|
21
|
-
},
|
|
22
|
+
}, _e = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]" }, ve = {
|
|
22
23
|
id: "tpl-saved-blocks-browser-title",
|
|
23
24
|
class: "tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]"
|
|
24
|
-
},
|
|
25
|
+
}, ye = ["aria-label"], be = { class: "tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden" }, xe = { class: "tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden" }, Se = { class: "tpl:flex tpl:flex-col tpl:gap-2 tpl:px-4 tpl:pt-4 tpl:pb-3" }, Ce = { class: "tpl:relative" }, we = ["placeholder", "disabled"], Te = ["aria-label"], T = { value: "" }, E = ["value"], D = { id: "tpl-saved-block-browser-categories" }, O = ["value"], Ee = { class: "tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4" }, De = ["aria-label"], Oe = {
|
|
25
26
|
key: 1,
|
|
26
27
|
class: "tpl:flex tpl:flex-col tpl:gap-1"
|
|
27
|
-
},
|
|
28
|
+
}, ke = ["onFocusout"], Ae = ["aria-label", "onKeydown"], je = [
|
|
28
29
|
"aria-label",
|
|
29
30
|
"placeholder",
|
|
30
31
|
"onKeydown"
|
|
31
|
-
],
|
|
32
|
+
], Me = ["aria-pressed", "onClick"], Ne = { class: "tpl:flex tpl:items-center tpl:gap-2" }, Pe = { class: "tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Fe = { class: "tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]" }, Ie = {
|
|
32
33
|
key: 0,
|
|
33
34
|
class: "tpl:mt-1 tpl:flex tpl:items-center"
|
|
34
|
-
},
|
|
35
|
+
}, Le = {
|
|
35
36
|
"data-testid": "saved-block-category",
|
|
36
37
|
class: "tpl:max-w-full tpl:truncate tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]"
|
|
37
|
-
},
|
|
38
|
+
}, Re = { class: "tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1" }, ze = {
|
|
38
39
|
key: 0,
|
|
39
40
|
class: "tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]"
|
|
40
|
-
},
|
|
41
|
+
}, Be = ["title"], Ve = ["aria-label", "onClick"], He = [
|
|
41
42
|
"aria-label",
|
|
42
43
|
"title",
|
|
43
44
|
"onClick"
|
|
44
|
-
],
|
|
45
|
+
], Ue = [
|
|
45
46
|
"aria-label",
|
|
46
47
|
"title",
|
|
47
48
|
"onClick"
|
|
48
|
-
],
|
|
49
|
+
], We = {
|
|
49
50
|
key: 2,
|
|
50
51
|
class: "tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12"
|
|
51
|
-
},
|
|
52
|
+
}, k = {
|
|
52
53
|
key: 0,
|
|
53
54
|
class: "tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]"
|
|
54
|
-
},
|
|
55
|
+
}, Ge = { class: "tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, Ke = { class: "tpl:mt-1 tpl:max-w-[220px] tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, qe = { class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]" }, Je = {
|
|
55
56
|
key: 0,
|
|
56
57
|
class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden"
|
|
57
|
-
},
|
|
58
|
+
}, Ye = { class: "tpl:flex-1 tpl:overflow-y-auto tpl:p-4" }, Xe = {
|
|
58
59
|
key: 1,
|
|
59
60
|
class: "tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4"
|
|
60
|
-
},
|
|
61
|
+
}, Ze = { class: "tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, Qe = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]" }, $e = { class: "tpl:flex tpl:items-center tpl:gap-2" }, et = { class: "tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]" }, tt = ["value"], nt = { class: "tpl:flex tpl:gap-2" }, rt = ["disabled"], A = /* @__PURE__ */ o({
|
|
61
62
|
__name: "SavedBlocksBrowserModal",
|
|
62
63
|
props: { visible: { type: Boolean } },
|
|
63
64
|
emits: ["close", "insert"],
|
|
64
|
-
setup(
|
|
65
|
-
let
|
|
65
|
+
setup(o, { emit: A }) {
|
|
66
|
+
let it = o, j = A, at = re(() => import("./BlockPreviewCanvas-CSIxs7hr.js").then((e) => e.n)), { t: M, format: N } = se(), P = S(ae, "SavedBlocksBrowserModal"), F = S(oe, "SavedBlocksBrowserModal"), ot = t(ie, void 0), I = a(""), L = a(""), R = a(null), z = a(null), B = a(null), V = a(""), H = a(""), U = a(null), W = a("end"), G = p(() => {
|
|
66
67
|
let e = I.value.trim().toLowerCase(), t = L.value;
|
|
67
68
|
return P.savedBlocks.value.filter((n) => !(e && !n.name.toLowerCase().includes(e) || t && n.category !== t));
|
|
68
|
-
}), K =
|
|
69
|
+
}), K = p(() => P.isLoading.value && P.savedBlocks.value.length === 0), st = p(() => I.value.trim().length > 0 || L.value !== ""), q = p(() => R.value ? P.savedBlocks.value.find((e) => e.id === R.value) ?? null : null), ct = p(() => {
|
|
69
70
|
let e = [{
|
|
70
71
|
value: "beginning",
|
|
71
72
|
label: M.savedBlocks.insertAtBeginning
|
|
@@ -81,13 +82,13 @@ var me = {
|
|
|
81
82
|
value: "end",
|
|
82
83
|
label: M.savedBlocks.insertAtEnd
|
|
83
84
|
}), e;
|
|
84
|
-
}),
|
|
85
|
+
}), lt = p(() => {
|
|
85
86
|
if (W.value === "end") return;
|
|
86
87
|
if (W.value === "beginning") return 0;
|
|
87
88
|
let e = F.content.value.blocks.findIndex((e) => e.id === W.value);
|
|
88
89
|
if (e !== -1) return e + 1;
|
|
89
90
|
});
|
|
90
|
-
e(() =>
|
|
91
|
+
e(() => it.visible, (e) => {
|
|
91
92
|
if (e) {
|
|
92
93
|
I.value = "", L.value = "", R.value = null, z.value = null, B.value = null;
|
|
93
94
|
let e = F.state.selectedBlockId;
|
|
@@ -97,11 +98,11 @@ var me = {
|
|
|
97
98
|
} else W.value = "end";
|
|
98
99
|
}
|
|
99
100
|
}, { immediate: !0 });
|
|
100
|
-
function
|
|
101
|
+
function ut(e) {
|
|
101
102
|
let t = [], n = /* @__PURE__ */ new Set();
|
|
102
|
-
for (let r of e.content) if (!n.has(r.type) &&
|
|
103
|
+
for (let r of e.content) if (!n.has(r.type) && w[r.type] && (n.add(r.type), t.push({
|
|
103
104
|
type: r.type,
|
|
104
|
-
icon:
|
|
105
|
+
icon: w[r.type]
|
|
105
106
|
})), t.length >= 5) break;
|
|
106
107
|
return t;
|
|
107
108
|
}
|
|
@@ -111,22 +112,20 @@ var me = {
|
|
|
111
112
|
}
|
|
112
113
|
function Y(e) {
|
|
113
114
|
let t = e.updatedAt ?? e.createdAt;
|
|
114
|
-
return t ?
|
|
115
|
+
return t ? me(t, M.time, N) ?? "" : "";
|
|
115
116
|
}
|
|
116
|
-
function
|
|
117
|
+
function dt(e) {
|
|
117
118
|
let t = e.updatedAt ?? e.createdAt;
|
|
118
|
-
|
|
119
|
-
let n = new Date(t);
|
|
120
|
-
return Number.isNaN(n.getTime()) ? "" : n.toLocaleString();
|
|
119
|
+
return t ? pe(t, ot) : "";
|
|
121
120
|
}
|
|
122
|
-
async function
|
|
121
|
+
async function ft(e) {
|
|
123
122
|
try {
|
|
124
123
|
await P.remove(e), R.value === e && (R.value = null);
|
|
125
124
|
} finally {
|
|
126
125
|
z.value = null;
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
|
-
async function
|
|
128
|
+
async function pt(e) {
|
|
130
129
|
P.canUpdateBlock(e) && (B.value = e.id, V.value = e.name, H.value = e.category ?? "", z.value = null, await te(), U.value?.focus(), U.value?.select());
|
|
131
130
|
}
|
|
132
131
|
function X() {
|
|
@@ -145,92 +144,92 @@ var me = {
|
|
|
145
144
|
X();
|
|
146
145
|
}
|
|
147
146
|
}
|
|
148
|
-
function
|
|
147
|
+
function mt(e, t) {
|
|
149
148
|
let n = e.currentTarget, r = e.relatedTarget;
|
|
150
149
|
r && n.contains(r) || Z(t);
|
|
151
150
|
}
|
|
152
151
|
function Q() {
|
|
153
|
-
q.value && j("insert", q.value,
|
|
152
|
+
q.value && j("insert", q.value, lt.value);
|
|
154
153
|
}
|
|
155
154
|
function $() {
|
|
156
155
|
j("close");
|
|
157
156
|
}
|
|
158
|
-
function
|
|
157
|
+
function ht(e) {
|
|
159
158
|
B.value || (e.key === "Escape" && $(), e.key === "Enter" && q.value && (e.preventDefault(), Q()));
|
|
160
159
|
}
|
|
161
|
-
return (e,
|
|
162
|
-
visible:
|
|
160
|
+
return (e, t) => (i(), _(he, {
|
|
161
|
+
visible: o.visible,
|
|
163
162
|
onClose: $,
|
|
164
|
-
onKeydown:
|
|
163
|
+
onKeydown: ht
|
|
165
164
|
}, {
|
|
166
|
-
default:
|
|
167
|
-
|
|
168
|
-
"aria-label":
|
|
165
|
+
default: r(() => [y("div", ge, [
|
|
166
|
+
y("div", _e, [y("h3", ve, x(l(M).savedBlocks.browse), 1), y("button", {
|
|
167
|
+
"aria-label": l(M).savedBlocks.close,
|
|
169
168
|
class: "tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]",
|
|
170
169
|
onClick: $
|
|
171
|
-
}, [l(
|
|
170
|
+
}, [u(l(de), {
|
|
172
171
|
size: 16,
|
|
173
172
|
"stroke-width": 2
|
|
174
|
-
})], 8,
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
})], 8, ye)]),
|
|
174
|
+
y("div", be, [y("div", xe, [
|
|
175
|
+
y("div", Se, [y("div", Ce, [u(l(le), {
|
|
177
176
|
size: 14,
|
|
178
177
|
"stroke-width": 2,
|
|
179
178
|
class: "tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]"
|
|
180
|
-
}),
|
|
181
|
-
"onUpdate:modelValue":
|
|
179
|
+
}), s(y("input", {
|
|
180
|
+
"onUpdate:modelValue": t[0] ||= (e) => I.value = e,
|
|
182
181
|
type: "text",
|
|
183
|
-
placeholder:
|
|
182
|
+
placeholder: l(M).savedBlocks.search,
|
|
184
183
|
disabled: K.value,
|
|
185
184
|
class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
|
|
186
|
-
}, null, 8,
|
|
185
|
+
}, null, 8, we), [[v, I.value]])]), l(P).categories.value.length > 0 ? s((i(), m("select", {
|
|
187
186
|
key: 0,
|
|
188
|
-
"onUpdate:modelValue":
|
|
187
|
+
"onUpdate:modelValue": t[1] ||= (e) => L.value = e,
|
|
189
188
|
"data-testid": "saved-blocks-category-filter",
|
|
190
|
-
"aria-label":
|
|
189
|
+
"aria-label": l(M).savedBlocks.filterByCategory,
|
|
191
190
|
class: "tpl:h-8 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
|
|
192
|
-
}, [
|
|
191
|
+
}, [y("option", T, x(l(M).savedBlocks.allCategories), 1), (i(!0), m(b, null, n(l(P).categories.value, (e) => (i(), m("option", {
|
|
193
192
|
key: e,
|
|
194
193
|
value: e
|
|
195
|
-
},
|
|
196
|
-
|
|
194
|
+
}, x(e), 9, E))), 128))], 8, Te)), [[c, L.value]]) : h("", !0)]),
|
|
195
|
+
y("datalist", D, [(i(!0), m(b, null, n(l(P).categories.value, (e) => (i(), m("option", {
|
|
197
196
|
key: e,
|
|
198
197
|
value: e
|
|
199
|
-
}, null, 8,
|
|
200
|
-
|
|
198
|
+
}, null, 8, O))), 128))]),
|
|
199
|
+
y("div", Ee, [K.value ? (i(), m("div", {
|
|
201
200
|
key: 0,
|
|
202
201
|
"data-testid": "saved-blocks-loading",
|
|
203
202
|
class: "tpl:flex tpl:flex-col tpl:gap-1",
|
|
204
203
|
role: "status",
|
|
205
204
|
"aria-busy": "true",
|
|
206
|
-
"aria-label":
|
|
207
|
-
}, [(
|
|
205
|
+
"aria-label": l(M).savedBlocks.loading
|
|
206
|
+
}, [(i(), m(b, null, n(3, (e) => y("div", {
|
|
208
207
|
key: e,
|
|
209
208
|
"aria-hidden": "true",
|
|
210
209
|
class: "tpl-saved-block-skeleton tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)]"
|
|
211
|
-
}, [...
|
|
210
|
+
}, [...t[7] ||= [y("div", { class: "tpl:h-3 tpl:w-1/2 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]" }, null, -1), y("div", { class: "tpl:mt-2 tpl:h-2.5 tpl:w-1/4 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]" }, null, -1)]])), 64))], 8, De)) : G.value.length > 0 ? (i(), m("div", Oe, [(i(!0), m(b, null, n(G.value, (e) => (i(), m(b, { key: e.id }, [B.value === e.id ? (i(), m("div", {
|
|
212
211
|
key: 0,
|
|
213
212
|
class: "tpl:flex tpl:w-full tpl:flex-col tpl:gap-1 tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-primary)]",
|
|
214
213
|
style: { "background-color": "var(--tpl-primary-light)" },
|
|
215
|
-
onFocusout: (t) =>
|
|
216
|
-
}, [
|
|
214
|
+
onFocusout: (t) => mt(t, e.id)
|
|
215
|
+
}, [s(y("input", {
|
|
217
216
|
ref_for: !0,
|
|
218
217
|
ref: (e) => U.value = e,
|
|
219
|
-
"onUpdate:modelValue":
|
|
218
|
+
"onUpdate:modelValue": t[2] ||= (e) => V.value = e,
|
|
220
219
|
type: "text",
|
|
221
|
-
"aria-label":
|
|
220
|
+
"aria-label": l(M).savedBlocks.rename,
|
|
222
221
|
class: "tpl:h-7 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]",
|
|
223
|
-
onKeydown: [
|
|
224
|
-
}, null, 40,
|
|
225
|
-
"onUpdate:modelValue":
|
|
222
|
+
onKeydown: [d(g((t) => Z(e.id), ["prevent", "stop"]), ["enter"]), t[3] ||= d(g((e) => X(), ["prevent", "stop"]), ["esc"])]
|
|
223
|
+
}, null, 40, Ae), [[v, V.value]]), s(y("input", {
|
|
224
|
+
"onUpdate:modelValue": t[4] ||= (e) => H.value = e,
|
|
226
225
|
type: "text",
|
|
227
226
|
"data-testid": "saved-blocks-edit-category",
|
|
228
|
-
"aria-label":
|
|
229
|
-
placeholder:
|
|
227
|
+
"aria-label": l(M).savedBlocks.category,
|
|
228
|
+
placeholder: l(M).savedBlocks.categoryPlaceholder,
|
|
230
229
|
list: "tpl-saved-block-browser-categories",
|
|
231
230
|
class: "tpl:h-7 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]",
|
|
232
|
-
onKeydown: [
|
|
233
|
-
}, null, 40,
|
|
231
|
+
onKeydown: [d(g((t) => Z(e.id), ["prevent", "stop"]), ["enter"]), t[5] ||= d(g((e) => X(), ["prevent", "stop"]), ["esc"])]
|
|
232
|
+
}, null, 40, je), [[v, H.value]])], 40, ke)) : (i(), m("button", {
|
|
234
233
|
key: 1,
|
|
235
234
|
type: "button",
|
|
236
235
|
"data-testid": "saved-block-card",
|
|
@@ -242,75 +241,75 @@ var me = {
|
|
|
242
241
|
}),
|
|
243
242
|
onClick: (t) => R.value = e.id
|
|
244
243
|
}, [
|
|
245
|
-
|
|
246
|
-
e.category ? (
|
|
247
|
-
|
|
248
|
-
(
|
|
244
|
+
y("div", Ne, [y("span", Pe, x(e.name), 1), y("span", Fe, x(l(N)(l(M).savedBlocks.blockCount, { count: e.content.length })), 1)]),
|
|
245
|
+
e.category ? (i(), m("div", Ie, [y("span", Le, x(e.category), 1)])) : h("", !0),
|
|
246
|
+
y("div", Re, [
|
|
247
|
+
(i(!0), m(b, null, n(ut(e), (e) => (i(), _(ee(e.icon), {
|
|
249
248
|
key: e.type,
|
|
250
249
|
size: 14,
|
|
251
250
|
"stroke-width": 1.5,
|
|
252
251
|
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
253
252
|
}))), 128)),
|
|
254
|
-
J(e) > 0 ? (
|
|
255
|
-
Y(e) ? (
|
|
253
|
+
J(e) > 0 ? (i(), m("span", ze, " +" + x(J(e)), 1)) : h("", !0),
|
|
254
|
+
Y(e) ? (i(), m("span", {
|
|
256
255
|
key: 1,
|
|
257
256
|
"data-testid": "saved-block-updated",
|
|
258
257
|
class: "tpl:ml-1 tpl:truncate tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]",
|
|
259
|
-
title:
|
|
260
|
-
},
|
|
261
|
-
z.value === e.id ? (
|
|
258
|
+
title: dt(e)
|
|
259
|
+
}, x(Y(e)), 9, Be)) : h("", !0),
|
|
260
|
+
z.value === e.id ? (i(), m("button", {
|
|
262
261
|
key: 2,
|
|
263
|
-
"aria-label":
|
|
262
|
+
"aria-label": l(M).savedBlocks.deleteConfirm,
|
|
264
263
|
class: "tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]",
|
|
265
264
|
style: { "background-color": "transparent" },
|
|
266
|
-
onClick:
|
|
267
|
-
},
|
|
265
|
+
onClick: g((t) => ft(e.id), ["stop"])
|
|
266
|
+
}, x(l(M).savedBlocks.deleteConfirm), 9, Ve)) : (i(), m(b, { key: 3 }, [l(P).canUpdateBlock(e) ? (i(), m("button", {
|
|
268
267
|
key: 0,
|
|
269
268
|
class: "tpl-saved-block-rename-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]",
|
|
270
|
-
"aria-label":
|
|
271
|
-
title:
|
|
272
|
-
onClick:
|
|
273
|
-
}, [l(
|
|
269
|
+
"aria-label": l(M).savedBlocks.rename,
|
|
270
|
+
title: l(M).savedBlocks.rename,
|
|
271
|
+
onClick: g((t) => pt(e), ["stop"])
|
|
272
|
+
}, [u(l(ce), {
|
|
274
273
|
size: 12,
|
|
275
274
|
"stroke-width": 1.5
|
|
276
|
-
})], 8,
|
|
275
|
+
})], 8, He)) : h("", !0), l(P).canDeleteBlock(e) ? (i(), m("button", {
|
|
277
276
|
key: 1,
|
|
278
|
-
class:
|
|
279
|
-
"aria-label":
|
|
280
|
-
title:
|
|
281
|
-
onClick:
|
|
282
|
-
}, [l(
|
|
277
|
+
class: f(["tpl-saved-block-delete-btn tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]", { "tpl:ml-auto": !l(P).canUpdateBlock(e) }]),
|
|
278
|
+
"aria-label": l(M).savedBlocks.delete,
|
|
279
|
+
title: l(M).savedBlocks.delete,
|
|
280
|
+
onClick: g((t) => z.value = e.id, ["stop"])
|
|
281
|
+
}, [u(l(ue), {
|
|
283
282
|
size: 12,
|
|
284
283
|
"stroke-width": 1.5
|
|
285
|
-
})], 10,
|
|
284
|
+
})], 10, Ue)) : h("", !0)], 64))
|
|
286
285
|
])
|
|
287
|
-
], 12,
|
|
286
|
+
], 12, Me))], 64))), 128))])) : (i(), m("div", We, [u(l(C), {
|
|
288
287
|
size: 32,
|
|
289
288
|
"stroke-width": 1,
|
|
290
289
|
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
291
|
-
}),
|
|
292
|
-
]),
|
|
290
|
+
}), st.value ? (i(), m("p", k, x(l(M).savedBlocks.noResults), 1)) : (i(), m(b, { key: 1 }, [y("p", Ge, x(l(M).savedBlocks.empty), 1), y("p", Ke, x(l(M).savedBlocks.emptyHint), 1)], 64))]))])
|
|
291
|
+
]), y("div", qe, [q.value ? (i(), m("div", Je, [y("div", Ye, [u(l(at), { blocks: q.value.content }, null, 8, ["blocks"])])])) : (i(), m("div", Xe, [u(l(C), {
|
|
293
292
|
size: 32,
|
|
294
293
|
"stroke-width": 1,
|
|
295
294
|
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
296
|
-
}),
|
|
297
|
-
|
|
298
|
-
"onUpdate:modelValue":
|
|
295
|
+
}), y("p", Ze, x(l(M).savedBlocks.selectToPreview), 1)]))])]),
|
|
296
|
+
y("div", Qe, [y("div", $e, [y("label", et, x(l(M).savedBlocks.insertPosition), 1), s(y("select", {
|
|
297
|
+
"onUpdate:modelValue": t[6] ||= (e) => W.value = e,
|
|
299
298
|
class: "tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
|
|
300
|
-
}, [(
|
|
299
|
+
}, [(i(!0), m(b, null, n(ct.value, (e) => (i(), m("option", {
|
|
301
300
|
key: e.value,
|
|
302
301
|
value: e.value
|
|
303
|
-
},
|
|
302
|
+
}, x(e.label), 9, tt))), 128))], 512), [[c, W.value]])]), y("div", nt, [y("button", {
|
|
304
303
|
type: "button",
|
|
305
304
|
"data-testid": "saved-blocks-browser-close",
|
|
306
305
|
class: "tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]",
|
|
307
306
|
onClick: $
|
|
308
|
-
},
|
|
307
|
+
}, x(l(M).savedBlocks.close), 1), y("button", {
|
|
309
308
|
type: "button",
|
|
310
|
-
class:
|
|
309
|
+
class: f(l(fe)),
|
|
311
310
|
disabled: !q.value,
|
|
312
311
|
onClick: Q
|
|
313
|
-
},
|
|
312
|
+
}, x(l(M).savedBlocks.insert), 11, rt)])])
|
|
314
313
|
])]),
|
|
315
314
|
_: 1
|
|
316
315
|
}, 8, ["visible"]));
|
|
@@ -319,4 +318,4 @@ var me = {
|
|
|
319
318
|
//#endregion
|
|
320
319
|
export { A as default };
|
|
321
320
|
|
|
322
|
-
//# sourceMappingURL=SavedBlocksBrowserModal-
|
|
321
|
+
//# sourceMappingURL=SavedBlocksBrowserModal-qC7UhW74.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SavedBlocksBrowserModal-qC7UhW74.js","names":[],"sources":["../../../src/components/SavedBlocksBrowserModal.vue","../../../src/components/SavedBlocksBrowserModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { primaryBtnClass } from \"../constants/styleConstants\";\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../composables\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { formatAbsoluteDateTime } from \"../utils/formatAbsoluteDateTime\";\nimport { formatRelativeTime } from \"../utils/formatRelativeTime\";\nimport {\n SAVED_BLOCKS_KEY,\n EDITOR_KEY,\n UI_LOCALE_KEY,\n requireInject,\n} from \"../keys\";\nimport type { SavedBlock } from \"@templatical/types\";\nimport { Package, Pencil, Search, Trash2, X } from \"@lucide/vue\";\nimport {\n computed,\n defineAsyncComponent,\n inject,\n nextTick,\n ref,\n watch,\n} from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\", saved: SavedBlock, insertIndex: number | undefined): void;\n}>();\n\nconst BlockPreviewCanvas = defineAsyncComponent(\n () => import(\"./BlockPreviewCanvas.vue\"),\n);\n\nconst { t, format } = useI18n();\nconst savedBlocks = requireInject(SAVED_BLOCKS_KEY, \"SavedBlocksBrowserModal\");\nconst editor = requireInject(EDITOR_KEY, \"SavedBlocksBrowserModal\");\nconst uiLocale = inject(UI_LOCALE_KEY, undefined);\n\nconst searchQuery = ref(\"\");\n/** `\"\"` = no category filter. Matched exactly against `SavedBlock.category`. */\nconst categoryFilter = ref(\"\");\nconst selectedId = ref<string | null>(null);\nconst confirmDeleteId = ref<string | null>(null);\nconst renamingId = ref<string | null>(null);\nconst renameDraft = ref(\"\");\nconst renameCategoryDraft = ref(\"\");\nconst renameInput = ref<HTMLInputElement | null>(null);\n// 'end' = append, 'beginning' = index 0, or block id = after that block\nconst insertPosition = ref<string>(\"end\");\n\n// Order is the provider's to decide — the editor never re-sorts. Whatever\n// `list()` returns is what the user sees (the bundled localStorage adapter, for\n// instance, returns newest-first). Filters only narrow; they never reorder.\n//\n// Both filters run in memory over the loaded entries rather than round-tripping\n// to the provider: that keeps a BYO provider at four dumb methods, and it is\n// what lets the category options below be derived at all — a provider-filtered\n// response could not tell us which other categories exist.\nconst filtered = computed(() => {\n const query = searchQuery.value.trim().toLowerCase();\n const category = categoryFilter.value;\n return savedBlocks.savedBlocks.value.filter((b) => {\n if (query && !b.name.toLowerCase().includes(query)) return false;\n if (category && b.category !== category) return false;\n return true;\n });\n});\n\n/**\n * The list is fetched when this modal opens, not at editor mount, so a first\n * open can arrive before the provider answers. Show the skeleton only while\n * there is *nothing* to display: on a reopen the previous entries render\n * immediately and the refetch lands underneath them.\n *\n * Without this the empty state would claim \"No saved blocks yet\" for the whole\n * duration of the request — false, not merely unhelpful.\n */\nconst isInitialLoad = computed(\n () =>\n savedBlocks.isLoading.value && savedBlocks.savedBlocks.value.length === 0,\n);\n\n/** Whether any filter is narrowing the list — drives the empty-state copy. */\nconst isFiltering = computed(\n () => searchQuery.value.trim().length > 0 || categoryFilter.value !== \"\",\n);\n\nconst selected = computed(() => {\n if (!selectedId.value) return null;\n return (\n savedBlocks.savedBlocks.value.find((b) => b.id === selectedId.value) ?? null\n );\n});\n\ninterface PositionOption {\n value: string;\n label: string;\n}\n\nconst positionOptions = computed<PositionOption[]>(() => {\n const options: PositionOption[] = [\n { value: \"beginning\", label: t.savedBlocks.insertAtBeginning },\n ];\n const blocks = editor.content.value.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n options.push({\n value: block.id,\n label: format(t.savedBlocks.insertAfterBlock, {\n block: `${label} ${i + 1}`,\n }),\n });\n }\n options.push({ value: \"end\", label: t.savedBlocks.insertAtEnd });\n return options;\n});\n\nconst resolvedInsertIndex = computed<number | undefined>(() => {\n if (insertPosition.value === \"end\") return undefined;\n if (insertPosition.value === \"beginning\") return 0;\n const blocks = editor.content.value.blocks;\n const idx = blocks.findIndex((b) => b.id === insertPosition.value);\n if (idx !== -1) return idx + 1;\n return undefined;\n});\n\n// `immediate` matters: the modal is mounted lazily behind a `v-if` on the same\n// state that drives `visible`, so it can mount with `visible` already true and\n// would otherwise never resolve the default insert position.\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n searchQuery.value = \"\";\n categoryFilter.value = \"\";\n selectedId.value = null;\n confirmDeleteId.value = null;\n renamingId.value = null;\n // Default to after selected block, or end\n const selectedBlockId = editor.state.selectedBlockId;\n if (selectedBlockId) {\n const idx = editor.content.value.blocks.findIndex(\n (b) => b.id === selectedBlockId,\n );\n insertPosition.value = idx !== -1 ? selectedBlockId : \"end\";\n } else {\n insertPosition.value = \"end\";\n }\n }\n },\n { immediate: true },\n);\n\nfunction getBlockTypeIcons(\n saved: SavedBlock,\n): { type: string; icon: unknown }[] {\n const icons: { type: string; icon: unknown }[] = [];\n const seen = new Set<string>();\n for (const block of saved.content) {\n if (!seen.has(block.type) && blockTypeIcons[block.type]) {\n seen.add(block.type);\n icons.push({ type: block.type, icon: blockTypeIcons[block.type] });\n }\n if (icons.length >= 5) break;\n }\n return icons;\n}\n\nfunction getRemainingTypeCount(saved: SavedBlock): number {\n const types = new Set(saved.content.map((b) => b.type));\n return Math.max(0, types.size - 5);\n}\n\n/** Compact \"5m ago\" label, or \"\" when the provider supplies no usable timestamp. */\nfunction relativeLabel(saved: SavedBlock): string {\n const raw = saved.updatedAt ?? saved.createdAt;\n if (!raw) return \"\";\n return formatRelativeTime(raw, t.time, format) ?? \"\";\n}\n\n/**\n * Absolute timestamp for the row's tooltip. Built by `Intl`, so it needs no\n * i18n keys — but it does need the editor's locale, or it formats in the\n * browser's and disagrees with the translated row beside it.\n */\nfunction absoluteLabel(saved: SavedBlock): string {\n const raw = saved.updatedAt ?? saved.createdAt;\n if (!raw) return \"\";\n return formatAbsoluteDateTime(raw, uiLocale);\n}\n\nasync function handleDelete(id: string): Promise<void> {\n try {\n await savedBlocks.remove(id);\n if (selectedId.value === id) {\n selectedId.value = null;\n }\n } finally {\n confirmDeleteId.value = null;\n }\n}\n\nasync function startRename(saved: SavedBlock): Promise<void> {\n // The pencil is already hidden when editing isn't permitted; this stops a\n // programmatic call opening an editor whose commit could only fail.\n if (!savedBlocks.canUpdateBlock(saved)) return;\n renamingId.value = saved.id;\n renameDraft.value = saved.name;\n renameCategoryDraft.value = saved.category ?? \"\";\n confirmDeleteId.value = null;\n await nextTick();\n renameInput.value?.focus();\n renameInput.value?.select();\n}\n\nfunction cancelRename(): void {\n renamingId.value = null;\n renameDraft.value = \"\";\n renameCategoryDraft.value = \"\";\n}\n\nasync function commitRename(id: string): Promise<void> {\n // Guards two races: Escape sets `renamingId` to null and the unmounting\n // input can still emit `blur`, and Enter commits then blurs. Either would\n // otherwise fire a second, unwanted update.\n if (renamingId.value !== id) return;\n const nextName = renameDraft.value.trim();\n const nextCategory = renameCategoryDraft.value.trim();\n const current = savedBlocks.savedBlocks.value.find((b) => b.id === id);\n\n // An empty name is not a rename — fall back to what's stored, so clearing\n // the field and blurring can't wipe the entry's name.\n const patch: Partial<{ name: string; category: string }> = {};\n if (nextName && nextName !== current?.name) patch.name = nextName;\n // \"\" is meaningful here: it clears the category. Only skip when unchanged.\n if (nextCategory !== (current?.category ?? \"\")) patch.category = nextCategory;\n\n // Skip the round-trip when nothing actually changed.\n if (Object.keys(patch).length === 0) {\n cancelRename();\n return;\n }\n try {\n await savedBlocks.update(id, patch);\n } finally {\n cancelRename();\n }\n}\n\n/**\n * Commit when focus leaves the edit row entirely — not when it moves between\n * the name and category inputs. A plain `@blur` on each input would commit\n * (and unmount the row) the moment the user tabbed from one to the other.\n */\nfunction onEditFocusOut(event: FocusEvent, id: string): void {\n const row = event.currentTarget as HTMLElement;\n const next = event.relatedTarget as Node | null;\n if (next && row.contains(next)) return;\n void commitRename(id);\n}\n\nfunction handleInsert(): void {\n if (selected.value) {\n emit(\"insert\", selected.value, resolvedInsertIndex.value);\n }\n}\n\nfunction handleClose(): void {\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n // While renaming, Enter/Escape belong to the inline input, not the dialog.\n if (renamingId.value) return;\n if (event.key === \"Escape\") {\n handleClose();\n }\n if (event.key === \"Enter\" && selected.value) {\n event.preventDefault();\n handleInsert();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <!-- Explicit width, not `w-full`: the modal's parent is shrink-to-fit, so\n `width: 100%` resolved against its own contents and the 600px preview\n canvas drove the dialog's size — it measured 523px with nothing selected\n and 965px with a block selected, nearly doubling on click. A pinned\n width makes every state identical and stops any future right-pane\n content from moving it. `max-w-full` keeps it inside the backdrop on\n narrow hosts — a percentage, so it neither reintroduces the `rem`\n sensitivity of issue #209 nor the viewport mismatch of issue #575; the\n preview scrolls within. -->\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n data-testid=\"saved-blocks-browser\"\n aria-labelledby=\"tpl-saved-blocks-browser-title\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-[1000px] tpl:max-w-full tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)]\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]\"\n >\n <h3\n id=\"tpl-saved-blocks-browser-title\"\n class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.savedBlocks.browse }}\n </h3>\n <button\n :aria-label=\"t.savedBlocks.close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]\"\n @click=\"handleClose\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left panel: saved block list -->\n <div\n class=\"tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Search + category filter -->\n <div\n class=\"tpl:flex tpl:flex-col tpl:gap-2 tpl:px-4 tpl:pt-4 tpl:pb-3\"\n >\n <div class=\"tpl:relative\">\n <Search\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]\"\n />\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n :placeholder=\"t.savedBlocks.search\"\n :disabled=\"isInitialLoad\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n </div>\n <!-- Only worth showing once something is actually categorised. -->\n <select\n v-if=\"savedBlocks.categories.value.length > 0\"\n v-model=\"categoryFilter\"\n data-testid=\"saved-blocks-category-filter\"\n :aria-label=\"t.savedBlocks.filterByCategory\"\n class=\"tpl:h-8 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option value=\"\">{{ t.savedBlocks.allCategories }}</option>\n <option\n v-for=\"option in savedBlocks.categories.value\"\n :key=\"option\"\n :value=\"option\"\n >\n {{ option }}\n </option>\n </select>\n </div>\n\n <!-- Suggestions for the inline category editor, shared by every row -->\n <datalist id=\"tpl-saved-block-browser-categories\">\n <option\n v-for=\"option in savedBlocks.categories.value\"\n :key=\"option\"\n :value=\"option\"\n />\n </datalist>\n\n <!-- List -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4\">\n <!-- Skeleton rows: same height and gap as a real card, so the pane\n doesn't resize when the entries land. `aria-busy` on the region\n is what a screen reader announces; the bars are decorative. -->\n <div\n v-if=\"isInitialLoad\"\n data-testid=\"saved-blocks-loading\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n role=\"status\"\n aria-busy=\"true\"\n :aria-label=\"t.savedBlocks.loading\"\n >\n <div\n v-for=\"n in 3\"\n :key=\"n\"\n aria-hidden=\"true\"\n class=\"tpl-saved-block-skeleton tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl:h-3 tpl:w-1/2 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n <div\n class=\"tpl:mt-2 tpl:h-2.5 tpl:w-1/4 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n </div>\n </div>\n\n <div\n v-else-if=\"filtered.length > 0\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n >\n <template v-for=\"item in filtered\" :key=\"item.id\">\n <!-- Inline rename replaces the whole row: an <input> nested\n inside the card <button> would be invalid markup and\n wouldn't focus reliably. -->\n <div\n v-if=\"renamingId === item.id\"\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:gap-1 tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-primary)]\"\n style=\"background-color: var(--tpl-primary-light)\"\n @focusout=\"onEditFocusOut($event, item.id)\"\n >\n <input\n :ref=\"(el) => (renameInput = el as HTMLInputElement | null)\"\n v-model=\"renameDraft\"\n type=\"text\"\n :aria-label=\"t.savedBlocks.rename\"\n class=\"tpl:h-7 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n @keydown.enter.prevent.stop=\"commitRename(item.id)\"\n @keydown.esc.prevent.stop=\"cancelRename()\"\n />\n <input\n v-model=\"renameCategoryDraft\"\n type=\"text\"\n data-testid=\"saved-blocks-edit-category\"\n :aria-label=\"t.savedBlocks.category\"\n :placeholder=\"t.savedBlocks.categoryPlaceholder\"\n list=\"tpl-saved-block-browser-categories\"\n class=\"tpl:h-7 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n @keydown.enter.prevent.stop=\"commitRename(item.id)\"\n @keydown.esc.prevent.stop=\"cancelRename()\"\n />\n </div>\n\n <button\n v-else\n type=\"button\"\n data-testid=\"saved-block-card\"\n :aria-pressed=\"selectedId === item.id\"\n class=\"tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]\"\n :style=\"{\n borderColor:\n selectedId === item.id\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border)',\n backgroundColor:\n selectedId === item.id\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"selectedId = item.id\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <span\n class=\"tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ item.name }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n format(t.savedBlocks.blockCount, {\n count: item.content.length,\n })\n }}\n </span>\n </div>\n <div\n v-if=\"item.category\"\n class=\"tpl:mt-1 tpl:flex tpl:items-center\"\n >\n <span\n data-testid=\"saved-block-category\"\n class=\"tpl:max-w-full tpl:truncate tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]\"\n >\n {{ item.category }}\n </span>\n </div>\n <div class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1\">\n <component\n :is=\"icon.icon\"\n v-for=\"icon in getBlockTypeIcons(item)\"\n :key=\"icon.type\"\n :size=\"14\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n v-if=\"getRemainingTypeCount(item) > 0\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n +{{ getRemainingTypeCount(item) }}\n </span>\n <span\n v-if=\"relativeLabel(item)\"\n data-testid=\"saved-block-updated\"\n class=\"tpl:ml-1 tpl:truncate tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n :title=\"absoluteLabel(item)\"\n >\n {{ relativeLabel(item) }}\n </span>\n <button\n v-if=\"confirmDeleteId === item.id\"\n :aria-label=\"t.savedBlocks.deleteConfirm\"\n class=\"tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]\"\n style=\"background-color: transparent\"\n @click.stop=\"handleDelete(item.id)\"\n >\n {{ t.savedBlocks.deleteConfirm }}\n </button>\n <!-- Hidden, not disabled: an action the user can't perform\n is better absent than greyed out. `ml-auto` moves to\n whichever button survives so the row stays aligned. -->\n <template v-else>\n <button\n v-if=\"savedBlocks.canUpdateBlock(item)\"\n class=\"tpl-saved-block-rename-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]\"\n :aria-label=\"t.savedBlocks.rename\"\n :title=\"t.savedBlocks.rename\"\n @click.stop=\"startRename(item)\"\n >\n <Pencil :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n <button\n v-if=\"savedBlocks.canDeleteBlock(item)\"\n class=\"tpl-saved-block-delete-btn tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]\"\n :class=\"{\n 'tpl:ml-auto': !savedBlocks.canUpdateBlock(item),\n }\"\n :aria-label=\"t.savedBlocks.delete\"\n :title=\"t.savedBlocks.delete\"\n @click.stop=\"confirmDeleteId = item.id\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n </template>\n </div>\n </button>\n </template>\n </div>\n\n <!-- Empty state — only once we actually know the library is empty -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n v-if=\"isFiltering\"\n class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.noResults }}\n </p>\n <template v-else>\n <p class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{ t.savedBlocks.empty }}\n </p>\n <p\n class=\"tpl:mt-1 tpl:max-w-[220px] tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.emptyHint }}\n </p>\n </template>\n </div>\n </div>\n </div>\n\n <!-- Right panel: preview -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"selected\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Visual preview -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <BlockPreviewCanvas :blocks=\"selected.content\" />\n </div>\n </div>\n\n <!-- Empty preview state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.selectToPreview }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label\n class=\"tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.insertPosition }}\n </label>\n <select\n v-model=\"insertPosition\"\n class=\"tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"opt in positionOptions\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"saved-blocks-browser-close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"handleClose\"\n >\n {{ t.savedBlocks.close }}\n </button>\n <button\n type=\"button\"\n :class=\"primaryBtnClass\"\n :disabled=\"!selected\"\n @click=\"handleInsert\"\n >\n {{ t.savedBlocks.insert }}\n </button>\n </div>\n </div>\n </div>\n </TplModal>\n</template>\n\n<style>\n/* Subtle breathing so the placeholders read as \"loading\" rather than as empty\n rows. `prefers-reduced-motion` holds them still at full opacity — the bars\n still communicate shape without animating. */\n.tpl-saved-block-skeleton {\n animation: tpl-saved-block-pulse 1.4s ease-in-out infinite;\n}\n\n@keyframes tpl-saved-block-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.55;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-saved-block-skeleton {\n animation: none;\n }\n}\n\n.tpl-saved-block-delete-btn:hover {\n color: var(--tpl-danger) !important;\n}\n\n.tpl-saved-block-rename-btn:hover {\n color: var(--tpl-primary) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { primaryBtnClass } from \"../constants/styleConstants\";\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../composables\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { formatAbsoluteDateTime } from \"../utils/formatAbsoluteDateTime\";\nimport { formatRelativeTime } from \"../utils/formatRelativeTime\";\nimport {\n SAVED_BLOCKS_KEY,\n EDITOR_KEY,\n UI_LOCALE_KEY,\n requireInject,\n} from \"../keys\";\nimport type { SavedBlock } from \"@templatical/types\";\nimport { Package, Pencil, Search, Trash2, X } from \"@lucide/vue\";\nimport {\n computed,\n defineAsyncComponent,\n inject,\n nextTick,\n ref,\n watch,\n} from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\", saved: SavedBlock, insertIndex: number | undefined): void;\n}>();\n\nconst BlockPreviewCanvas = defineAsyncComponent(\n () => import(\"./BlockPreviewCanvas.vue\"),\n);\n\nconst { t, format } = useI18n();\nconst savedBlocks = requireInject(SAVED_BLOCKS_KEY, \"SavedBlocksBrowserModal\");\nconst editor = requireInject(EDITOR_KEY, \"SavedBlocksBrowserModal\");\nconst uiLocale = inject(UI_LOCALE_KEY, undefined);\n\nconst searchQuery = ref(\"\");\n/** `\"\"` = no category filter. Matched exactly against `SavedBlock.category`. */\nconst categoryFilter = ref(\"\");\nconst selectedId = ref<string | null>(null);\nconst confirmDeleteId = ref<string | null>(null);\nconst renamingId = ref<string | null>(null);\nconst renameDraft = ref(\"\");\nconst renameCategoryDraft = ref(\"\");\nconst renameInput = ref<HTMLInputElement | null>(null);\n// 'end' = append, 'beginning' = index 0, or block id = after that block\nconst insertPosition = ref<string>(\"end\");\n\n// Order is the provider's to decide — the editor never re-sorts. Whatever\n// `list()` returns is what the user sees (the bundled localStorage adapter, for\n// instance, returns newest-first). Filters only narrow; they never reorder.\n//\n// Both filters run in memory over the loaded entries rather than round-tripping\n// to the provider: that keeps a BYO provider at four dumb methods, and it is\n// what lets the category options below be derived at all — a provider-filtered\n// response could not tell us which other categories exist.\nconst filtered = computed(() => {\n const query = searchQuery.value.trim().toLowerCase();\n const category = categoryFilter.value;\n return savedBlocks.savedBlocks.value.filter((b) => {\n if (query && !b.name.toLowerCase().includes(query)) return false;\n if (category && b.category !== category) return false;\n return true;\n });\n});\n\n/**\n * The list is fetched when this modal opens, not at editor mount, so a first\n * open can arrive before the provider answers. Show the skeleton only while\n * there is *nothing* to display: on a reopen the previous entries render\n * immediately and the refetch lands underneath them.\n *\n * Without this the empty state would claim \"No saved blocks yet\" for the whole\n * duration of the request — false, not merely unhelpful.\n */\nconst isInitialLoad = computed(\n () =>\n savedBlocks.isLoading.value && savedBlocks.savedBlocks.value.length === 0,\n);\n\n/** Whether any filter is narrowing the list — drives the empty-state copy. */\nconst isFiltering = computed(\n () => searchQuery.value.trim().length > 0 || categoryFilter.value !== \"\",\n);\n\nconst selected = computed(() => {\n if (!selectedId.value) return null;\n return (\n savedBlocks.savedBlocks.value.find((b) => b.id === selectedId.value) ?? null\n );\n});\n\ninterface PositionOption {\n value: string;\n label: string;\n}\n\nconst positionOptions = computed<PositionOption[]>(() => {\n const options: PositionOption[] = [\n { value: \"beginning\", label: t.savedBlocks.insertAtBeginning },\n ];\n const blocks = editor.content.value.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n options.push({\n value: block.id,\n label: format(t.savedBlocks.insertAfterBlock, {\n block: `${label} ${i + 1}`,\n }),\n });\n }\n options.push({ value: \"end\", label: t.savedBlocks.insertAtEnd });\n return options;\n});\n\nconst resolvedInsertIndex = computed<number | undefined>(() => {\n if (insertPosition.value === \"end\") return undefined;\n if (insertPosition.value === \"beginning\") return 0;\n const blocks = editor.content.value.blocks;\n const idx = blocks.findIndex((b) => b.id === insertPosition.value);\n if (idx !== -1) return idx + 1;\n return undefined;\n});\n\n// `immediate` matters: the modal is mounted lazily behind a `v-if` on the same\n// state that drives `visible`, so it can mount with `visible` already true and\n// would otherwise never resolve the default insert position.\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n searchQuery.value = \"\";\n categoryFilter.value = \"\";\n selectedId.value = null;\n confirmDeleteId.value = null;\n renamingId.value = null;\n // Default to after selected block, or end\n const selectedBlockId = editor.state.selectedBlockId;\n if (selectedBlockId) {\n const idx = editor.content.value.blocks.findIndex(\n (b) => b.id === selectedBlockId,\n );\n insertPosition.value = idx !== -1 ? selectedBlockId : \"end\";\n } else {\n insertPosition.value = \"end\";\n }\n }\n },\n { immediate: true },\n);\n\nfunction getBlockTypeIcons(\n saved: SavedBlock,\n): { type: string; icon: unknown }[] {\n const icons: { type: string; icon: unknown }[] = [];\n const seen = new Set<string>();\n for (const block of saved.content) {\n if (!seen.has(block.type) && blockTypeIcons[block.type]) {\n seen.add(block.type);\n icons.push({ type: block.type, icon: blockTypeIcons[block.type] });\n }\n if (icons.length >= 5) break;\n }\n return icons;\n}\n\nfunction getRemainingTypeCount(saved: SavedBlock): number {\n const types = new Set(saved.content.map((b) => b.type));\n return Math.max(0, types.size - 5);\n}\n\n/** Compact \"5m ago\" label, or \"\" when the provider supplies no usable timestamp. */\nfunction relativeLabel(saved: SavedBlock): string {\n const raw = saved.updatedAt ?? saved.createdAt;\n if (!raw) return \"\";\n return formatRelativeTime(raw, t.time, format) ?? \"\";\n}\n\n/**\n * Absolute timestamp for the row's tooltip. Built by `Intl`, so it needs no\n * i18n keys — but it does need the editor's locale, or it formats in the\n * browser's and disagrees with the translated row beside it.\n */\nfunction absoluteLabel(saved: SavedBlock): string {\n const raw = saved.updatedAt ?? saved.createdAt;\n if (!raw) return \"\";\n return formatAbsoluteDateTime(raw, uiLocale);\n}\n\nasync function handleDelete(id: string): Promise<void> {\n try {\n await savedBlocks.remove(id);\n if (selectedId.value === id) {\n selectedId.value = null;\n }\n } finally {\n confirmDeleteId.value = null;\n }\n}\n\nasync function startRename(saved: SavedBlock): Promise<void> {\n // The pencil is already hidden when editing isn't permitted; this stops a\n // programmatic call opening an editor whose commit could only fail.\n if (!savedBlocks.canUpdateBlock(saved)) return;\n renamingId.value = saved.id;\n renameDraft.value = saved.name;\n renameCategoryDraft.value = saved.category ?? \"\";\n confirmDeleteId.value = null;\n await nextTick();\n renameInput.value?.focus();\n renameInput.value?.select();\n}\n\nfunction cancelRename(): void {\n renamingId.value = null;\n renameDraft.value = \"\";\n renameCategoryDraft.value = \"\";\n}\n\nasync function commitRename(id: string): Promise<void> {\n // Guards two races: Escape sets `renamingId` to null and the unmounting\n // input can still emit `blur`, and Enter commits then blurs. Either would\n // otherwise fire a second, unwanted update.\n if (renamingId.value !== id) return;\n const nextName = renameDraft.value.trim();\n const nextCategory = renameCategoryDraft.value.trim();\n const current = savedBlocks.savedBlocks.value.find((b) => b.id === id);\n\n // An empty name is not a rename — fall back to what's stored, so clearing\n // the field and blurring can't wipe the entry's name.\n const patch: Partial<{ name: string; category: string }> = {};\n if (nextName && nextName !== current?.name) patch.name = nextName;\n // \"\" is meaningful here: it clears the category. Only skip when unchanged.\n if (nextCategory !== (current?.category ?? \"\")) patch.category = nextCategory;\n\n // Skip the round-trip when nothing actually changed.\n if (Object.keys(patch).length === 0) {\n cancelRename();\n return;\n }\n try {\n await savedBlocks.update(id, patch);\n } finally {\n cancelRename();\n }\n}\n\n/**\n * Commit when focus leaves the edit row entirely — not when it moves between\n * the name and category inputs. A plain `@blur` on each input would commit\n * (and unmount the row) the moment the user tabbed from one to the other.\n */\nfunction onEditFocusOut(event: FocusEvent, id: string): void {\n const row = event.currentTarget as HTMLElement;\n const next = event.relatedTarget as Node | null;\n if (next && row.contains(next)) return;\n void commitRename(id);\n}\n\nfunction handleInsert(): void {\n if (selected.value) {\n emit(\"insert\", selected.value, resolvedInsertIndex.value);\n }\n}\n\nfunction handleClose(): void {\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n // While renaming, Enter/Escape belong to the inline input, not the dialog.\n if (renamingId.value) return;\n if (event.key === \"Escape\") {\n handleClose();\n }\n if (event.key === \"Enter\" && selected.value) {\n event.preventDefault();\n handleInsert();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <!-- Explicit width, not `w-full`: the modal's parent is shrink-to-fit, so\n `width: 100%` resolved against its own contents and the 600px preview\n canvas drove the dialog's size — it measured 523px with nothing selected\n and 965px with a block selected, nearly doubling on click. A pinned\n width makes every state identical and stops any future right-pane\n content from moving it. `max-w-full` keeps it inside the backdrop on\n narrow hosts — a percentage, so it neither reintroduces the `rem`\n sensitivity of issue #209 nor the viewport mismatch of issue #575; the\n preview scrolls within. -->\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n data-testid=\"saved-blocks-browser\"\n aria-labelledby=\"tpl-saved-blocks-browser-title\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-[1000px] tpl:max-w-full tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)]\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]\"\n >\n <h3\n id=\"tpl-saved-blocks-browser-title\"\n class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.savedBlocks.browse }}\n </h3>\n <button\n :aria-label=\"t.savedBlocks.close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]\"\n @click=\"handleClose\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left panel: saved block list -->\n <div\n class=\"tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Search + category filter -->\n <div\n class=\"tpl:flex tpl:flex-col tpl:gap-2 tpl:px-4 tpl:pt-4 tpl:pb-3\"\n >\n <div class=\"tpl:relative\">\n <Search\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]\"\n />\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n :placeholder=\"t.savedBlocks.search\"\n :disabled=\"isInitialLoad\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n </div>\n <!-- Only worth showing once something is actually categorised. -->\n <select\n v-if=\"savedBlocks.categories.value.length > 0\"\n v-model=\"categoryFilter\"\n data-testid=\"saved-blocks-category-filter\"\n :aria-label=\"t.savedBlocks.filterByCategory\"\n class=\"tpl:h-8 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option value=\"\">{{ t.savedBlocks.allCategories }}</option>\n <option\n v-for=\"option in savedBlocks.categories.value\"\n :key=\"option\"\n :value=\"option\"\n >\n {{ option }}\n </option>\n </select>\n </div>\n\n <!-- Suggestions for the inline category editor, shared by every row -->\n <datalist id=\"tpl-saved-block-browser-categories\">\n <option\n v-for=\"option in savedBlocks.categories.value\"\n :key=\"option\"\n :value=\"option\"\n />\n </datalist>\n\n <!-- List -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4\">\n <!-- Skeleton rows: same height and gap as a real card, so the pane\n doesn't resize when the entries land. `aria-busy` on the region\n is what a screen reader announces; the bars are decorative. -->\n <div\n v-if=\"isInitialLoad\"\n data-testid=\"saved-blocks-loading\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n role=\"status\"\n aria-busy=\"true\"\n :aria-label=\"t.savedBlocks.loading\"\n >\n <div\n v-for=\"n in 3\"\n :key=\"n\"\n aria-hidden=\"true\"\n class=\"tpl-saved-block-skeleton tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl:h-3 tpl:w-1/2 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n <div\n class=\"tpl:mt-2 tpl:h-2.5 tpl:w-1/4 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n </div>\n </div>\n\n <div\n v-else-if=\"filtered.length > 0\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n >\n <template v-for=\"item in filtered\" :key=\"item.id\">\n <!-- Inline rename replaces the whole row: an <input> nested\n inside the card <button> would be invalid markup and\n wouldn't focus reliably. -->\n <div\n v-if=\"renamingId === item.id\"\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:gap-1 tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-primary)]\"\n style=\"background-color: var(--tpl-primary-light)\"\n @focusout=\"onEditFocusOut($event, item.id)\"\n >\n <input\n :ref=\"(el) => (renameInput = el as HTMLInputElement | null)\"\n v-model=\"renameDraft\"\n type=\"text\"\n :aria-label=\"t.savedBlocks.rename\"\n class=\"tpl:h-7 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n @keydown.enter.prevent.stop=\"commitRename(item.id)\"\n @keydown.esc.prevent.stop=\"cancelRename()\"\n />\n <input\n v-model=\"renameCategoryDraft\"\n type=\"text\"\n data-testid=\"saved-blocks-edit-category\"\n :aria-label=\"t.savedBlocks.category\"\n :placeholder=\"t.savedBlocks.categoryPlaceholder\"\n list=\"tpl-saved-block-browser-categories\"\n class=\"tpl:h-7 tpl:w-full tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n @keydown.enter.prevent.stop=\"commitRename(item.id)\"\n @keydown.esc.prevent.stop=\"cancelRename()\"\n />\n </div>\n\n <button\n v-else\n type=\"button\"\n data-testid=\"saved-block-card\"\n :aria-pressed=\"selectedId === item.id\"\n class=\"tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]\"\n :style=\"{\n borderColor:\n selectedId === item.id\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border)',\n backgroundColor:\n selectedId === item.id\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"selectedId = item.id\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <span\n class=\"tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ item.name }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n format(t.savedBlocks.blockCount, {\n count: item.content.length,\n })\n }}\n </span>\n </div>\n <div\n v-if=\"item.category\"\n class=\"tpl:mt-1 tpl:flex tpl:items-center\"\n >\n <span\n data-testid=\"saved-block-category\"\n class=\"tpl:max-w-full tpl:truncate tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]\"\n >\n {{ item.category }}\n </span>\n </div>\n <div class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1\">\n <component\n :is=\"icon.icon\"\n v-for=\"icon in getBlockTypeIcons(item)\"\n :key=\"icon.type\"\n :size=\"14\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n v-if=\"getRemainingTypeCount(item) > 0\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n +{{ getRemainingTypeCount(item) }}\n </span>\n <span\n v-if=\"relativeLabel(item)\"\n data-testid=\"saved-block-updated\"\n class=\"tpl:ml-1 tpl:truncate tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n :title=\"absoluteLabel(item)\"\n >\n {{ relativeLabel(item) }}\n </span>\n <button\n v-if=\"confirmDeleteId === item.id\"\n :aria-label=\"t.savedBlocks.deleteConfirm\"\n class=\"tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]\"\n style=\"background-color: transparent\"\n @click.stop=\"handleDelete(item.id)\"\n >\n {{ t.savedBlocks.deleteConfirm }}\n </button>\n <!-- Hidden, not disabled: an action the user can't perform\n is better absent than greyed out. `ml-auto` moves to\n whichever button survives so the row stays aligned. -->\n <template v-else>\n <button\n v-if=\"savedBlocks.canUpdateBlock(item)\"\n class=\"tpl-saved-block-rename-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]\"\n :aria-label=\"t.savedBlocks.rename\"\n :title=\"t.savedBlocks.rename\"\n @click.stop=\"startRename(item)\"\n >\n <Pencil :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n <button\n v-if=\"savedBlocks.canDeleteBlock(item)\"\n class=\"tpl-saved-block-delete-btn tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-dim)]\"\n :class=\"{\n 'tpl:ml-auto': !savedBlocks.canUpdateBlock(item),\n }\"\n :aria-label=\"t.savedBlocks.delete\"\n :title=\"t.savedBlocks.delete\"\n @click.stop=\"confirmDeleteId = item.id\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n </template>\n </div>\n </button>\n </template>\n </div>\n\n <!-- Empty state — only once we actually know the library is empty -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n v-if=\"isFiltering\"\n class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.noResults }}\n </p>\n <template v-else>\n <p class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{ t.savedBlocks.empty }}\n </p>\n <p\n class=\"tpl:mt-1 tpl:max-w-[220px] tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.emptyHint }}\n </p>\n </template>\n </div>\n </div>\n </div>\n\n <!-- Right panel: preview -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"selected\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Visual preview -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <BlockPreviewCanvas :blocks=\"selected.content\" />\n </div>\n </div>\n\n <!-- Empty preview state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.selectToPreview }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label\n class=\"tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.savedBlocks.insertPosition }}\n </label>\n <select\n v-model=\"insertPosition\"\n class=\"tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"opt in positionOptions\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"saved-blocks-browser-close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"handleClose\"\n >\n {{ t.savedBlocks.close }}\n </button>\n <button\n type=\"button\"\n :class=\"primaryBtnClass\"\n :disabled=\"!selected\"\n @click=\"handleInsert\"\n >\n {{ t.savedBlocks.insert }}\n </button>\n </div>\n </div>\n </div>\n </TplModal>\n</template>\n\n<style>\n/* Subtle breathing so the placeholders read as \"loading\" rather than as empty\n rows. `prefers-reduced-motion` holds them still at full opacity — the bars\n still communicate shape without animating. */\n.tpl-saved-block-skeleton {\n animation: tpl-saved-block-pulse 1.4s ease-in-out infinite;\n}\n\n@keyframes tpl-saved-block-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.55;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-saved-block-skeleton {\n animation: none;\n }\n}\n\n.tpl-saved-block-delete-btn:hover {\n color: var(--tpl-danger) !important;\n}\n\n.tpl-saved-block-rename-btn:hover {\n color: var(--tpl-primary) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { N as e, S as t, at as n, g as r, h as i, m as a, u as o, x as s } from "./draggable-
|
|
1
|
+
import { N as e, S as t, at as n, g as r, h as i, m as a, u as o, x as s } from "./draggable-kbOhCgnk.js";
|
|
2
2
|
//#endregion
|
|
3
3
|
//#region src/components/SavedBlocksPanels.vue
|
|
4
4
|
var c = /* @__PURE__ */ t({
|
|
5
5
|
__name: "SavedBlocksPanels",
|
|
6
6
|
props: { feature: {} },
|
|
7
7
|
setup(t) {
|
|
8
|
-
let c = t, l = s(() => import("./SavedBlocksPickBar-
|
|
8
|
+
let c = t, l = s(() => import("./SavedBlocksPickBar-CxXjF3RN.js")), u = s(() => import("./SaveBlockDialog-DWaeqcM8.js")), d = s(() => import("./SavedBlocksBrowserModal-qC7UhW74.js"));
|
|
9
9
|
function f(e, t) {
|
|
10
10
|
c.feature.insert(e, t);
|
|
11
11
|
}
|
|
@@ -35,4 +35,4 @@ var c = /* @__PURE__ */ t({
|
|
|
35
35
|
//#endregion
|
|
36
36
|
export { c as default };
|
|
37
37
|
|
|
38
|
-
//# sourceMappingURL=SavedBlocksPanels-
|
|
38
|
+
//# sourceMappingURL=SavedBlocksPanels-BvtjtdsY.js.map
|