@panphora/clayjs 1.23.0 → 1.24.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/all.js +1 -1
- package/dist/blockeditor/themes/light-1.css +431 -0
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +30285 -26613
- package/dist/m/c-277QP5ET.js +1 -0
- package/dist/m/c-2BLVA6IN.js +5 -0
- package/dist/m/c-2OJMOUPC.js +1 -0
- package/dist/m/c-2PVGJFI5.js +1 -0
- package/dist/m/{c-AYVGD6A6.js → c-3UW5EJ77.js} +1 -1
- package/dist/m/{c-T5NK2TVY.js → c-42ZNV7LI.js} +1 -1
- package/dist/m/{c-3TLG3OLG.js → c-46LJAZZJ.js} +1 -1
- package/dist/m/c-47IBJGPQ.js +1 -0
- package/dist/m/c-5FXEKU4Y.js +1 -0
- package/dist/m/c-5KXDKC5G.js +1 -0
- package/dist/m/{c-EUGJVHDD.js → c-5RZNFTWD.js} +1 -1
- package/dist/m/c-65LJU2H2.js +72 -0
- package/dist/m/{c-NQJRVJ56.js → c-6IJEVE6B.js} +1 -1
- package/dist/m/{c-NW54NHKE.js → c-6LEYVQJK.js} +1 -1
- package/dist/m/{c-GSRLXYWL.js → c-6Q6RGFBF.js} +1 -1
- package/dist/m/c-6RGEQNVV.js +1 -0
- package/dist/m/c-7CM4GMTS.js +6 -0
- package/dist/m/c-7NCWENHV.js +1 -0
- package/dist/m/c-7V6MGMX2.js +1 -0
- package/dist/m/{c-LKSL4AUJ.js → c-7WZSUTYP.js} +1 -1
- package/dist/m/c-A4L7APIU.js +1 -0
- package/dist/m/c-APGHGDZM.js +1 -0
- package/dist/m/c-AT7GLQGV.js +34 -0
- package/dist/m/{c-ZRXJD4G6.js → c-B7GOFGQR.js} +1 -1
- package/dist/m/{c-KDXIJEEF.js → c-BGN7RCTJ.js} +1 -1
- package/dist/m/c-BKM46LUJ.js +1 -0
- package/dist/m/c-BOMQIQ3V.js +1 -0
- package/dist/m/{c-62HZW5DH.js → c-C5AKJVRH.js} +1 -1
- package/dist/m/{c-BXYSDKAN.js → c-CJ7QYEWI.js} +1 -1
- package/dist/m/c-CTGRDT6E.js +1 -0
- package/dist/m/c-CV4T65JE.js +6 -0
- package/dist/m/c-CVSWLX3X.js +1 -0
- package/dist/m/c-CZHTXGJS.js +1 -0
- package/dist/m/c-D7NT5S4F.js +1 -0
- package/dist/m/c-DD26VXAP.js +1 -0
- package/dist/m/{c-ANNFZFLN.js → c-DDS23EY6.js} +1 -1
- package/dist/m/{c-ALDVFPWL.js → c-DFKBGSN2.js} +1 -1
- package/dist/m/c-DIKR7H57.js +147 -0
- package/dist/m/c-DXPOFLEJ.js +1 -0
- package/dist/m/c-EDE45NM7.js +1 -0
- package/dist/m/c-EILLN2H2.js +1 -0
- package/dist/m/c-EODTTNVA.js +1 -0
- package/dist/m/{c-G6COVIZB.js → c-ESK4S67I.js} +1 -1
- package/dist/m/c-FDWR3FL7.js +1 -0
- package/dist/m/{c-WQZTI5P2.js → c-FEC6LGFF.js} +1 -1
- package/dist/m/c-FLEIU6CG.js +1 -0
- package/dist/m/c-FQAQRXGK.js +1 -0
- package/dist/m/{c-W4JCVLAB.js → c-FSQXFG5T.js} +1 -1
- package/dist/m/c-FVPNX2GO.js +135 -0
- package/dist/m/c-FYDKYW6E.js +1 -0
- package/dist/m/c-GNI7WHKF.js +32 -0
- package/dist/m/c-GO635CRU.js +3 -0
- package/dist/m/c-H3FNLBHN.js +2 -0
- package/dist/m/c-HDVH3MVC.js +72 -0
- package/dist/m/c-HIX5H5L7.js +1 -0
- package/dist/m/c-HKSLOVU4.js +1 -0
- package/dist/m/{c-SGRIYOAJ.js → c-HL6BLJNA.js} +1 -1
- package/dist/m/c-HTLYXCUH.js +1 -0
- package/dist/m/{c-QIR5CAKM.js → c-IUL7PU4P.js} +1 -1
- package/dist/m/c-IXO7EI2Q.js +1 -0
- package/dist/m/{c-W5E5P2TZ.js → c-JQF2EUCG.js} +1 -1
- package/dist/m/{c-NEL7HQW3.js → c-JWDCQL6J.js} +1 -1
- package/dist/m/c-KQKKIVLW.js +20 -0
- package/dist/m/c-LATNQ6Q5.js +1 -0
- package/dist/m/c-LD6UVV5R.js +1 -0
- package/dist/m/c-LS7IVQVW.js +1 -0
- package/dist/m/{c-JCOQVZYS.js → c-LSVN3OLG.js} +1 -1
- package/dist/m/{c-JDZDF2QM.js → c-LTSPHVAK.js} +1 -1
- package/dist/m/c-MFOSMAVM.js +3 -0
- package/dist/m/c-MJ7GVE46.js +1 -0
- package/dist/m/c-MP6S4AZU.js +6 -0
- package/dist/m/c-MQKSKQHG.js +2 -0
- package/dist/m/{c-UHUNXWKN.js → c-MQYEDF3Q.js} +1 -1
- package/dist/m/{c-IHUWUKIU.js → c-MR75YPRT.js} +1 -1
- package/dist/m/c-ND3Q2G2I.js +1 -0
- package/dist/m/c-NEQRFC4O.js +1 -0
- package/dist/m/{c-5FKRZNJV.js → c-NFQFV7OP.js} +1 -1
- package/dist/m/c-NHXOBD6A.js +1 -0
- package/dist/m/{c-QIKT6SKM.js → c-NICICT4P.js} +1 -1
- package/dist/m/c-NQAPWM7O.js +1 -0
- package/dist/m/{c-QUGCKMWF.js → c-NVU3DORS.js} +1 -1
- package/dist/m/{c-LWIDDWAR.js → c-OCYG4NBQ.js} +22 -22
- package/dist/m/c-OH2FPYSY.js +19 -0
- package/dist/m/c-OYHI67B6.js +1 -0
- package/dist/m/{c-R2XUODVY.js → c-OYW5MFPZ.js} +1 -1
- package/dist/m/{c-5B4YBZXC.js → c-P2HKMJAW.js} +1 -1
- package/dist/m/{c-TSBVMWAH.js → c-PCNJETUA.js} +1 -1
- package/dist/m/c-PDBNCJCT.js +1 -0
- package/dist/m/c-PGSBXHUE.js +1 -0
- package/dist/m/{c-5TIZ2I64.js → c-PYG2NGWU.js} +1 -1
- package/dist/m/c-Q26XHBLQ.js +1 -0
- package/dist/m/c-Q6BG3ZLO.js +1 -0
- package/dist/m/c-QENKN7QI.js +1 -0
- package/dist/m/c-QQFHEODI.js +1 -0
- package/dist/m/c-QTEUO2K6.js +1 -0
- package/dist/m/c-R23UHMXG.js +1 -0
- package/dist/m/c-RETTCOWO.js +10 -0
- package/dist/m/c-SE3SIY7Y.js +7 -0
- package/dist/m/{c-XX75FFNL.js → c-SGGGCIVD.js} +1 -1
- package/dist/m/c-SGH3UTF3.js +1 -0
- package/dist/m/c-SIM7Z3U7.js +1 -0
- package/dist/m/{c-X5LNHET5.js → c-SKMIR5HQ.js} +1 -1
- package/dist/m/{c-R27DWQKX.js → c-STRI73GQ.js} +1 -1
- package/dist/m/c-SUXHUL7U.js +1 -0
- package/dist/m/c-SXGEWPFC.js +10 -0
- package/dist/m/c-SXNSSHPP.js +1 -0
- package/dist/m/c-T3P3AXEW.js +1 -0
- package/dist/m/{c-PXLTUYZC.js → c-TFLRT5JX.js} +1 -1
- package/dist/m/{c-YCLU4UNT.js → c-TGIMBAWE.js} +1 -1
- package/dist/m/c-TGSHHFO6.js +1 -0
- package/dist/m/c-U4NTUVDZ.js +1 -0
- package/dist/m/c-UOOIF2AL.js +1 -0
- package/dist/m/{c-6TYGD5AB.js → c-UOUUI265.js} +1 -1
- package/dist/m/c-V2U3F4QX.js +1 -0
- package/dist/m/c-V2V5FKXX.js +1 -0
- package/dist/m/c-VBXRXTVS.js +26 -0
- package/dist/m/c-VHSMPITY.js +1 -0
- package/dist/m/c-VPXCXBDT.js +1 -0
- package/dist/m/c-WCG6AKA7.js +1 -0
- package/dist/m/c-WNOLMHI2.js +1 -0
- package/dist/m/{c-CRPIDGXC.js → c-X6MOYATI.js} +1 -1
- package/dist/m/{c-FQGHR32O.js → c-X75G43SZ.js} +1 -1
- package/dist/m/c-XB6EAIAR.js +13 -0
- package/dist/m/c-XMIDHEKF.js +1 -0
- package/dist/m/c-XUZMRQ7O.js +4 -0
- package/dist/m/{c-2Z22FCFK.js → c-XW5TIYYB.js} +1 -1
- package/dist/m/c-YDW4VCHP.js +1 -0
- package/dist/m/{c-QGAPG7OM.js → c-YLHTD3KX.js} +1 -1
- package/dist/m/c-YNIEIPBS.js +1 -0
- package/dist/m/c-Z4G2UK6T.js +4 -0
- package/dist/m/c-Z6MROEQ4.js +1 -0
- package/dist/m/c-ZFO5RW6G.js +2 -0
- package/dist/m/c-ZGCO4J3H.js +8 -0
- package/dist/m/c-ZKKAF4UT.js +3 -0
- package/dist/m/c-ZRGEN6QQ.js +4 -0
- package/dist/m/core/{admin-attrs-U7IJNYBG.js → admin-attrs-ONRWVXPK.js} +1 -1
- package/dist/m/core/{autosave-F5YQCKVL.js → autosave-TBKAAAFB.js} +1 -1
- package/dist/m/core/conflict-notice-FOEE6I7E.js +1 -0
- package/dist/m/core/draft-mode-4MCL3LYI.js +1 -0
- package/dist/m/core/{host-permission-6EVJGNPL.js → host-permission-X4BENZEU.js} +1 -1
- package/dist/m/core/{is-edit-mode-GCX6767E.js → is-edit-mode-FFHCQ5DR.js} +1 -1
- package/dist/m/core/{persist-CJXIL7D4.js → persist-6ILUR34S.js} +1 -1
- package/dist/m/core/{recovery-DRFZRJED.js → recovery-APWADU4K.js} +1 -1
- package/dist/m/core/save-6HT2LABE.js +1 -0
- package/dist/m/core/save-core-EVMNZQ4V.js +1 -0
- package/dist/m/core/site-edits-G4BOZMV4.js +1 -0
- package/dist/m/core/{unsaved-warning-HNQVTA2D.js → unsaved-warning-ITZDIZ2I.js} +1 -1
- package/dist/m/internals/{index-NRPNDTSA.js → index-HS3CQDFW.js} +1 -1
- package/dist/m/loader-GLLVDUZA.js +1 -0
- package/dist/m/manifest-50FF1C45.js +1 -0
- package/dist/m/manifest-edit-BE1A621D.js +1 -0
- package/dist/m/manifest.json +2072 -1811
- package/dist/m/page/editor-KAZDJNND.js +307 -0
- package/dist/m/plugins/ai-edit-LABECCSN.js +6 -0
- package/dist/m/plugins/{cms-H7DTMUQX.js → cms-T2HLI7JG.js} +16 -16
- package/dist/m/plugins/{demo-42BSYQ7T.js → demo-T7B4RIKL.js} +1 -1
- package/dist/m/plugins/{derive-QVI3LI3A.js → derive-6LPJOCAV.js} +1 -1
- package/dist/m/plugins/indicator-J4ABFARX.js +1 -0
- package/dist/m/plugins/{live-DCUBKXY4.js → live-PGBIEMGB.js} +1 -1
- package/dist/m/plugins/menu-YZL3X2AJ.js +1 -0
- package/dist/m/plugins/page-WOCNX2IN.js +2 -0
- package/dist/m/plugins/page-view-SJODOOGE.js +1 -0
- package/dist/m/plugins/{people-5XRNRP5U.js → people-C4TCWFOQ.js} +1 -1
- package/dist/m/plugins/{quickcrop-WWF5I422.js → quickcrop-X5MRIBAP.js} +2 -2
- package/dist/m/plugins/richclay-Y2NV44NA.js +549 -0
- package/dist/m/plugins/sortable-XCHQG65Z.js +1 -0
- package/dist/m/plugins/{source-6BKMK4PO.js → source-YK7MO23W.js} +1 -1
- package/dist/m/plugins/{undo-3JEYK5QY.js → undo-VLRBQNPN.js} +1 -1
- package/dist/m/plugins/upgrade-NNTJBSK2.js +1 -0
- package/dist/m/plugins/{upload-TTCQRIM5.js → upload-ZKKJV76X.js} +1 -1
- package/dist/m/plugins/visitor-drafts-RTZNONHT.js +20 -0
- package/dist/m/plugins/{visitor-drafts-owner-Z5B2OQBR.js → visitor-drafts-owner-LHUHFU3L.js} +1 -1
- package/dist/m/plugins/{visitor-menu-4LQMKLPP.js → visitor-menu-FY4KM2SW.js} +1 -1
- package/dist/m/plugins/wire-M2NR354P.js +1 -0
- package/dist/m/{reader-core-POFQCKRV.js → reader-core-T2S3JC4E.js} +2 -2
- package/dist/m/sync/live-sync-Z64YREWQ.js +1 -0
- package/dist/m/ui/index-MGTWOW2C.js +1 -0
- package/dist/m/vendor/{sap-core.vendor-OA5SKL7Q.js → sap-core.vendor-QAEPMETX.js} +1 -1
- package/dist/offline-manifest.json +1250 -1082
- package/dist/server/core.cjs +132 -109
- package/dist/server/page-compaction-worker.cjs +5 -5
- package/dist/server/rooms.mjs +3 -1
- package/docs/guide.md +89 -44
- package/entries/clay-data.js +1 -1
- package/entries/sap.js +1 -1
- package/package.json +5 -3
- package/src/blockeditor/themes/content.css +70 -0
- package/src/blockeditor/themes/light-1.css +431 -0
- package/src/blockeditor/themes/light-1.source.css +270 -0
- package/src/blockeditor/themes/light-2.css +211 -0
- package/src/blockeditor/themes/light-3.css +130 -0
- package/src/blockeditor/themes/light-4.css +299 -0
- package/src/core/conflict-notice.js +28 -0
- package/src/core/draft-mode.js +6 -0
- package/src/core/host-meta.js +6 -6
- package/src/core/is-edit-mode.js +9 -3
- package/src/core/mode-support.js +3 -0
- package/src/core/mode-switch.js +4 -4
- package/src/core/mode-transition.js +3 -1
- package/src/core/page-format-wire.js +1 -1
- package/src/core/page-format.js +8 -8
- package/src/core/save-accepted.js +6 -7
- package/src/core/save-core.js +52 -23
- package/src/core/save-failure-notice.js +37 -0
- package/src/core/save-feedback.js +6 -0
- package/src/core/save-replace.js +1 -1
- package/src/core/save-size.js +2 -2
- package/src/core/save.js +78 -15
- package/src/core/site-edits.js +166 -0
- package/src/core/unsaved-warning.js +2 -2
- package/src/core/upgrade-run.js +4 -4
- package/src/lib/ai-comment.js +23 -0
- package/src/lib/effect.js +8 -0
- package/src/lib/idb-error-code.js +20 -0
- package/src/live/fast-lane.js +10 -10
- package/src/live/local-lane.js +10 -10
- package/src/live/page-compaction.js +8 -8
- package/src/live/server-lane.js +1 -1
- package/src/loader-logic.js +48 -18
- package/src/loader.js +11 -6
- package/src/page/adopt-boot.js +1 -1
- package/src/page/adopt.js +2 -2
- package/src/page/ai-area.js +182 -0
- package/src/page/ai-preview.js +80 -0
- package/src/page/ai-surface.js +257 -0
- package/src/page/ai-tool.js +308 -145
- package/src/page/announce.js +2 -2
- package/src/page/awareness.js +18 -18
- package/src/page/blocks.js +35 -34
- package/src/page/blocksel.js +2 -2
- package/src/page/board-drag.js +11 -11
- package/src/page/board-face.js +1 -1
- package/src/page/board-panel.js +26 -26
- package/src/page/board-style.js +127 -127
- package/src/page/board-view.js +48 -48
- package/src/page/chart.js +29 -29
- package/src/page/checkpoint-proof.js +19 -19
- package/src/page/chrome.js +74 -65
- package/src/page/clipboard-shape.js +1 -1
- package/src/page/csv-import.js +4 -4
- package/src/page/drag.js +5 -5
- package/src/page/editor.js +301 -296
- package/src/page/export-html.js +8 -8
- package/src/page/features/checklist.js +36 -36
- package/src/page/features/export-html.js +19 -19
- package/src/page/features/export-markdown.js +10 -10
- package/src/page/features/import-markdown.js +13 -13
- package/src/page/features/media.js +50 -50
- package/src/page/features/table.js +17 -17
- package/src/page/features/title-cover.js +17 -17
- package/src/page/features/title-icon.js +7 -7
- package/src/page/features/toc.js +1 -1
- package/src/page/features/tool.js +42 -18
- package/src/page/format-command.js +4 -4
- package/src/page/format-gate.js +6 -6
- package/src/page/format-host.js +6 -6
- package/src/page/format-level.js +3 -3
- package/src/page/format-native-commands.js +2 -2
- package/src/page/highlight-slot.js +1 -1
- package/src/page/highlight.js +4 -4
- package/src/page/history-intent.js +2 -2
- package/src/page/image-actions.js +4 -4
- package/src/page/link.js +10 -10
- package/src/page/media.js +3 -3
- package/src/page/mention-picker.js +7 -7
- package/src/page/merge.js +1 -1
- package/src/page/placeholder.js +3 -3
- package/src/page/projection.js +11 -11
- package/src/page/reader-patch.js +2 -2
- package/src/page/recover.js +26 -26
- package/src/page/recovery-ui.js +28 -28
- package/src/page/rich/chart.js +1 -1
- package/src/page/rich/page-tool.js +21 -6
- package/src/page/rich/tool-runtime.js +3 -2
- package/src/page/save-projection.js +2 -2
- package/src/page/save-states.js +9 -9
- package/src/page/schema.js +9 -9
- package/src/page/table-style.js +21 -21
- package/src/page/toc-link.js +1 -1
- package/src/page/tool-capability.js +8 -3
- package/src/page/tool-transport.js +52 -0
- package/src/page/tool-views.js +4 -0
- package/src/page/tools.js +6 -1
- package/src/page/view.js +6 -6
- package/src/page/ybind/binding.js +1 -1
- package/src/page/ybind/conversion.js +1 -1
- package/src/page/ybind/layout.js +4 -4
- package/src/page/ybind/migrate.js +1 -1
- package/src/page/ybind/origins.js +2 -2
- package/src/page/ybind/provider.js +2 -2
- package/src/page/ybind/recovery-proof.js +3 -3
- package/src/page/ybind/recovery-replay.js +2 -2
- package/src/plugins/ai-edit/picker.js +235 -0
- package/src/plugins/ai-edit.js +220 -34
- package/src/plugins/indicator-chip.js +39 -34
- package/src/plugins/indicator.js +13 -4
- package/src/plugins/live.js +21 -13
- package/src/plugins/menu/actions.js +89 -17
- package/src/plugins/menu/seo.js +32 -6
- package/src/plugins/menu/shell-style.js +26 -7
- package/src/plugins/menu/shell.js +12 -7
- package/src/plugins/menu/variants.js +6 -10
- package/src/plugins/menu/visitor.js +44 -34
- package/src/plugins/menu.js +3 -2
- package/src/plugins/page-view.js +2 -2
- package/src/plugins/page.js +257 -98
- package/src/plugins/sortable.js +7 -0
- package/src/plugins/upgrade.js +2 -1
- package/src/plugins/visitor-drafts/commit.js +303 -0
- package/src/plugins/visitor-drafts/idb.js +221 -0
- package/src/plugins/visitor-drafts/intro-style.js +1 -1
- package/src/plugins/visitor-drafts/intro.js +8 -25
- package/src/plugins/visitor-drafts/prompt.js +30 -4
- package/src/plugins/visitor-drafts/session.js +345 -0
- package/src/plugins/visitor-drafts/store.js +351 -32
- package/src/plugins/visitor-drafts.js +158 -27
- package/src/server/checkpoint/index.js +8 -8
- package/src/server/rooms/index.js +2 -2
- package/src/server/rooms/live-data-rooms.js +35 -35
- package/src/server/rooms/live-page-history.js +2 -2
- package/src/server/rooms/page-admission.js +34 -6
- package/src/server/rooms/page-conversion.js +1 -1
- package/src/server/rooms/page-lineage.js +4 -4
- package/src/server/rooms/page-rebase.js +4 -4
- package/src/server/rooms/page-room-conformance.js +14 -14
- package/src/server/rooms/page-state.js +2 -2
- package/src/server/rooms/page-write-admission.js +7 -7
- package/src/server/rooms/page-write-guard.js +3 -3
- package/src/sync/data-guard-notice.js +265 -0
- package/src/sync/live-sync.js +54 -11
- package/src/sync/page-relay.js +4 -4
- package/src/sync/presence-store.js +1 -1
- package/src/sync/presence-target.js +3 -3
- package/src/sync/presence.js +1 -1
- package/src/sync/stream.js +54 -13
- package/src/sync/wire-profile.js +8 -3
- package/src/ui/bevel.js +1 -1
- package/src/ui/index.js +9 -10
- package/src/ui/skins/cms.js +1 -1
- package/src/ui/skins/quickcrop.js +1 -1
- package/src/ui/skins/richclay.js +2 -2
- package/src/ui/toast-lazy.js +12 -3
- package/src/ui/toast.js +9 -3
- package/src/vendor/hyper-html-api-data.vendor.js +1 -1
- package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
- package/src/vendor/hypercms.vendor.js +16 -16
- package/src/vendor/richclay.vendor.js +48 -20
- package/src/vendor/sap-core.vendor.js +1 -1
- package/tool-generation/context.md +44 -0
- package/tool-generation/system.md +23 -0
- package/dist/m/c-24PMUJIV.js +0 -2
- package/dist/m/c-2AYVYEI4.js +0 -1
- package/dist/m/c-2AZDZWCK.js +0 -1
- package/dist/m/c-2L6O5PDS.js +0 -6
- package/dist/m/c-2RYOGUQM.js +0 -2
- package/dist/m/c-2ZVOEU4M.js +0 -1
- package/dist/m/c-37NAONZA.js +0 -1
- package/dist/m/c-3GJ4SDE2.js +0 -1
- package/dist/m/c-3O7OBDCM.js +0 -1
- package/dist/m/c-5OJ7YF2N.js +0 -1
- package/dist/m/c-5WMXEKFH.js +0 -135
- package/dist/m/c-6RDOKLN7.js +0 -1
- package/dist/m/c-A4IP6MBC.js +0 -1
- package/dist/m/c-A6L3FMUS.js +0 -1
- package/dist/m/c-ATKAOH23.js +0 -1
- package/dist/m/c-B6O7AJYG.js +0 -32
- package/dist/m/c-BCGWJYXW.js +0 -1
- package/dist/m/c-BGKAT2FL.js +0 -7
- package/dist/m/c-BYTKL6UX.js +0 -1
- package/dist/m/c-DHBRA6DL.js +0 -1
- package/dist/m/c-DTMZ3ZHI.js +0 -1
- package/dist/m/c-E3ZXARWA.js +0 -1
- package/dist/m/c-EK63G3CG.js +0 -1
- package/dist/m/c-ELYIIYRS.js +0 -1
- package/dist/m/c-EXKHPOVC.js +0 -3
- package/dist/m/c-FCSZ3DC5.js +0 -20
- package/dist/m/c-FTWGYI5E.js +0 -1
- package/dist/m/c-G4PWUDD5.js +0 -1
- package/dist/m/c-GBKB5WDL.js +0 -2
- package/dist/m/c-GBTVCJXC.js +0 -1
- package/dist/m/c-GC2HICFJ.js +0 -1
- package/dist/m/c-GRWMD6O5.js +0 -1
- package/dist/m/c-HCNWJE4A.js +0 -4
- package/dist/m/c-HG2C77BM.js +0 -4
- package/dist/m/c-JGHEACGK.js +0 -1
- package/dist/m/c-JGLX6TBK.js +0 -1
- package/dist/m/c-JNRKHYWO.js +0 -1
- package/dist/m/c-KBS3UV6B.js +0 -1
- package/dist/m/c-KQ7E3PYY.js +0 -1
- package/dist/m/c-L3F5ZLIV.js +0 -1
- package/dist/m/c-L4KMT7FO.js +0 -6
- package/dist/m/c-L5VFATT6.js +0 -1
- package/dist/m/c-LCYPMULG.js +0 -1
- package/dist/m/c-LIGQYZ7M.js +0 -6
- package/dist/m/c-LKIWEPAV.js +0 -1
- package/dist/m/c-MPSKIU2I.js +0 -1
- package/dist/m/c-N7JPUPGC.js +0 -1
- package/dist/m/c-NK33MMYF.js +0 -1
- package/dist/m/c-NQLWOHLH.js +0 -10
- package/dist/m/c-NTHUTABN.js +0 -1
- package/dist/m/c-NWX3MFT4.js +0 -1
- package/dist/m/c-OKXI52N7.js +0 -1
- package/dist/m/c-OPONJ2UK.js +0 -128
- package/dist/m/c-OWQWOGYI.js +0 -1
- package/dist/m/c-P6Q2ZTFQ.js +0 -1
- package/dist/m/c-QEN6HZCA.js +0 -1
- package/dist/m/c-QIXT3Q7C.js +0 -28
- package/dist/m/c-QM5LXRDT.js +0 -9
- package/dist/m/c-QYYUVLVG.js +0 -1
- package/dist/m/c-RQRMPZXD.js +0 -1
- package/dist/m/c-SBCBXKOZ.js +0 -26
- package/dist/m/c-SKF5HL4I.js +0 -4
- package/dist/m/c-SRPC3VBE.js +0 -1
- package/dist/m/c-TANBH7FI.js +0 -1
- package/dist/m/c-THGQQDYY.js +0 -1
- package/dist/m/c-UD5JZTDL.js +0 -1
- package/dist/m/c-UDGEFWNU.js +0 -1
- package/dist/m/c-UGNV5MFB.js +0 -1
- package/dist/m/c-VIRFF5HG.js +0 -8
- package/dist/m/c-VJYEVRYK.js +0 -1
- package/dist/m/c-VQCEOGKH.js +0 -13
- package/dist/m/c-VYJ7O4RI.js +0 -34
- package/dist/m/c-W7IPVCAA.js +0 -1
- package/dist/m/c-WBGWGOM7.js +0 -1
- package/dist/m/c-WJ5DRBHT.js +0 -72
- package/dist/m/c-X5WC4PX5.js +0 -1
- package/dist/m/c-XCFQEWW6.js +0 -3
- package/dist/m/c-XGH4GS2T.js +0 -1
- package/dist/m/c-XLQ3BDMM.js +0 -1
- package/dist/m/c-ZQXNHZZT.js +0 -72
- package/dist/m/core/conflict-notice-42H4ASLA.js +0 -1
- package/dist/m/core/draft-mode-WSQAT67S.js +0 -1
- package/dist/m/core/save-ESPSQPLV.js +0 -1
- package/dist/m/core/save-core-6SQHOR6W.js +0 -1
- package/dist/m/loader-FBDE2UAB.js +0 -1
- package/dist/m/manifest-1D2F5CA5.js +0 -1
- package/dist/m/manifest-edit-F7CC5CFD.js +0 -1
- package/dist/m/page/editor-OSKOW6RS.js +0 -304
- package/dist/m/plugins/ai-edit-VV4QTINH.js +0 -6
- package/dist/m/plugins/indicator-WVPGMOZ7.js +0 -1
- package/dist/m/plugins/menu-Q4MFFQ6E.js +0 -1
- package/dist/m/plugins/page-OAWUJQEL.js +0 -2
- package/dist/m/plugins/page-view-DZ26ZDDQ.js +0 -1
- package/dist/m/plugins/richclay-PGOOJ4OK.js +0 -517
- package/dist/m/plugins/sortable-266HY6TR.js +0 -1
- package/dist/m/plugins/upgrade-BNZVFHMZ.js +0 -1
- package/dist/m/plugins/visitor-drafts-2AUYKAIJ.js +0 -20
- package/dist/m/plugins/wire-W2EV36KZ.js +0 -1
- package/dist/m/sync/live-sync-XGUPHI3I.js +0 -1
- package/dist/m/ui/index-VGYAKAKI.js +0 -1
- package/dist/page/themes/light-1.css +0 -431
- package/src/page/themes/content.css +0 -70
- package/src/page/themes/light-1.css +0 -431
- package/src/page/themes/light-1.source.css +0 -270
- package/src/page/themes/light-2.css +0 -211
- package/src/page/themes/light-3.css +0 -130
- package/src/page/themes/light-4.css +0 -299
- /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
- /package/dist/m/plugins/{quickcrop-WWF5I422.js.LEGAL.txt → quickcrop-X5MRIBAP.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-PGOOJ4OK.js.LEGAL.txt → richclay-Y2NV44NA.js.LEGAL.txt} +0 -0
|
@@ -11,7 +11,7 @@ const lineages = new Map();
|
|
|
11
11
|
|
|
12
12
|
function storedArticle(html, doc) {
|
|
13
13
|
const $ = load(html);
|
|
14
|
-
const nodes = $('article[data-
|
|
14
|
+
const nodes = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
|
|
15
15
|
if (nodes.length !== 1) return null;
|
|
16
16
|
const article = doc.createElement('article');
|
|
17
17
|
for (const [name, value] of Object.entries(nodes[0].attribs)) article.setAttribute(name, value);
|
|
@@ -31,7 +31,7 @@ function storedLineage(html, createDocument) {
|
|
|
31
31
|
return lineage;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function checkInitialLineage({ currentHtml, html,
|
|
34
|
+
export function checkInitialLineage({ currentHtml, html, blockeditorWrite = null, createDocument }) {
|
|
35
35
|
const lineage = storedLineage(currentHtml, createDocument);
|
|
36
36
|
if (!lineage) return;
|
|
37
37
|
let doc;
|
|
@@ -42,7 +42,7 @@ export function checkInitialLineage({ currentHtml, html, pageWrite = null, creat
|
|
|
42
42
|
}
|
|
43
43
|
try {
|
|
44
44
|
if (lineage.client !== null && !Y.decodeStateVector(Y.encodeStateVector(doc)).has(lineage.client)) refuse('seed-lineage');
|
|
45
|
-
if (typeof
|
|
45
|
+
if (typeof blockeditorWrite === 'string' && blockeditorWrite.trim() === 'v1' && seedOnly(doc, lineage.client) &&
|
|
46
46
|
splitPage(html).file === currentHtml) refuse('nothing-to-write');
|
|
47
47
|
} finally {
|
|
48
48
|
doc.destroy();
|
|
@@ -51,6 +51,6 @@ export function checkInitialLineage({ currentHtml, html, pageWrite = null, creat
|
|
|
51
51
|
|
|
52
52
|
export function checkOrdinaryLineage({ currentHtml, html, room = null }) {
|
|
53
53
|
const before = inspectPage(currentHtml ?? '');
|
|
54
|
-
const stored = before.kind === 'yjs' ? before.tags[0][1] : room?.stores.get('
|
|
54
|
+
const stored = before.kind === 'yjs' ? before.tags[0][1] : room?.stores.get('blockeditor')?.value ?? null;
|
|
55
55
|
if (stored && !sharesLineage(stored, inspectPage(html).tags[0][1])) refuse('seed-lineage');
|
|
56
56
|
}
|
|
@@ -5,13 +5,13 @@ import { project, parseModel, renderModel, fragmentCleaner } from '../../page/pr
|
|
|
5
5
|
import { blocksFromPageValues } from '../../page/ybind/decode.js';
|
|
6
6
|
import { mergePageModels } from '../../page/merge.js';
|
|
7
7
|
import { inert, pageRev } from './page-state.js';
|
|
8
|
-
import {
|
|
8
|
+
import { blockeditorFormatFloor, withPageFormatFloor } from '../../page/format-level.js';
|
|
9
9
|
|
|
10
10
|
// The lone article of html: its element rebuilt in doc, and where its start tag
|
|
11
11
|
// and its contents sit in html.
|
|
12
12
|
export function articleOf(html, doc) {
|
|
13
13
|
const $ = load(html, { sourceCodeLocationInfo: true });
|
|
14
|
-
const nodes = $('article[data-
|
|
14
|
+
const nodes = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
|
|
15
15
|
if (nodes.length !== 1) return null;
|
|
16
16
|
const loc = nodes[0].sourceCodeLocation;
|
|
17
17
|
if (!loc?.startTag || !loc?.endTag) return null;
|
|
@@ -70,7 +70,7 @@ export function rebasePage({ storedHtml, state, incomingHtml, baseHtml, createDo
|
|
|
70
70
|
const body = incomingHtml.slice(0, incoming.startTag.startOffset) + open + inner + incomingHtml.slice(innerEnd);
|
|
71
71
|
// The floor is canonical metadata outside the article, and it only rises: a
|
|
72
72
|
// writer that rebased from a version before activation cannot lower it.
|
|
73
|
-
const html = withPageFormatFloor(body, Math.max(
|
|
73
|
+
const html = withPageFormatFloor(body, Math.max(blockeditorFormatFloor(storedHtml), blockeditorFormatFloor(incomingHtml)));
|
|
74
74
|
return { html, conflicts: merged.conflicts };
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -106,7 +106,7 @@ export function closestBase(candidates, incomingHtml, createDocument) {
|
|
|
106
106
|
export function stampPageRev(html, rev, floor = 0) {
|
|
107
107
|
if (typeof html !== 'string' || !rev) return null;
|
|
108
108
|
const $ = load(html, { sourceCodeLocationInfo: true });
|
|
109
|
-
const nodes = $('article[data-
|
|
109
|
+
const nodes = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
|
|
110
110
|
const loc = nodes.length === 1 ? nodes[0].sourceCodeLocation?.startTag : null;
|
|
111
111
|
if (!loc) return null;
|
|
112
112
|
const stamped = stampOpen(html, loc, rev);
|
|
@@ -6,7 +6,7 @@ import { entriesOf } from '../../page/ybind/parts.js';
|
|
|
6
6
|
import { rangesOf, within, docRanges } from '../../page/ybind/containment.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* The D13
|
|
9
|
+
* The D13 blockeditor-room conformance fixture: a seeded run of several tabs, restarts,
|
|
10
10
|
* resets and legacy file writes over one page room, driven through an adapter
|
|
11
11
|
* that stands in for a host's wiring of the room. Every run throws with the seed
|
|
12
12
|
* and the invariant it broke. The module reads no files: the caller passes the
|
|
@@ -39,14 +39,14 @@ import { rangesOf, within, docRanges } from '../../page/ybind/containment.js';
|
|
|
39
39
|
*
|
|
40
40
|
* readState({ key, tabId, capable })
|
|
41
41
|
* The JSON the client receives for a state read: { epoch, seq, writer,
|
|
42
|
-
* stores: {
|
|
42
|
+
* stores: { blockeditor: { value, ... }, ... }, ... }, with the page store's value
|
|
43
43
|
* as the client's re-read carries it. `tabId` is the reading client (null for
|
|
44
44
|
* a broadcast's read) and `capable` says whether it advertises the page
|
|
45
45
|
* compaction capability (default true). Answers { status: 404, body: null }
|
|
46
46
|
* when the host has no room for key.
|
|
47
47
|
*
|
|
48
48
|
* apply({ key, tabId, frame })
|
|
49
|
-
* Apply one frame { name, n, epoch,
|
|
49
|
+
* Apply one frame { name, n, epoch, blockeditorGeneration, compaction, ops } from
|
|
50
50
|
* tabId, atomically. Answer { status: 200, body: { duplicate, seq, epoch, ... } }
|
|
51
51
|
* when the host took it, { status, body: { error, ... } } with the host's
|
|
52
52
|
* status for a refusal (the error code and any retryAfter), or
|
|
@@ -82,7 +82,7 @@ import { rangesOf, within, docRanges } from '../../page/ybind/containment.js';
|
|
|
82
82
|
*/
|
|
83
83
|
|
|
84
84
|
const NET = 'net';
|
|
85
|
-
const TAG = /merge="
|
|
85
|
+
const TAG = /merge="blockeditor" clay="live"(?: live-at="[^"]*")?>(.*?)<\/script>/;
|
|
86
86
|
|
|
87
87
|
function rng(seed) {
|
|
88
88
|
let s = seed >>> 0 || 1;
|
|
@@ -167,7 +167,7 @@ export function createRoomsAdapter({ now = null, newEpoch = null, compaction = n
|
|
|
167
167
|
body: {
|
|
168
168
|
reported: rooms.used(),
|
|
169
169
|
receiptBytes: target.receiptBytes ?? 0,
|
|
170
|
-
transitionBytes: target.
|
|
170
|
+
transitionBytes: target.blockeditorTransition?.bytes ?? 0,
|
|
171
171
|
reservedBytes: target.compaction?.job?.reserved ?? 0,
|
|
172
172
|
stores,
|
|
173
173
|
},
|
|
@@ -206,7 +206,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
|
|
|
206
206
|
const r = rng(seed);
|
|
207
207
|
const pick = (n) => Math.floor(r() * n);
|
|
208
208
|
const limits = { activeBytes, loose: adapter.limits.loose, soft: adapter.limits.soft, leaseMs: adapter.limits.leaseMs, tailFloor };
|
|
209
|
-
const htmlOf = (value, at = null) => template.replace(TAG, () => `merge="
|
|
209
|
+
const htmlOf = (value, at = null) => template.replace(TAG, () => `merge="blockeditor" clay="live"${at ? ` live-at="${at}"` : ''}>${JSON.stringify(value)}</script>`);
|
|
210
210
|
const fail = (message, extra = {}) => { throw Object.assign(new Error(`seed ${seed}: ${message}`), extra); };
|
|
211
211
|
const dueSet = new Set();
|
|
212
212
|
adapter.onCompactionDue(({ key, overflow }) => { if (key === KEY) dueSet.add({ key, overflow }); });
|
|
@@ -218,7 +218,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
|
|
|
218
218
|
if (answer.status !== 200) fail(`state read refused with ${answer.status}`);
|
|
219
219
|
return answer.body;
|
|
220
220
|
};
|
|
221
|
-
const page = () => state().stores.
|
|
221
|
+
const page = () => state().stores.blockeditor.value;
|
|
222
222
|
const usage = () => {
|
|
223
223
|
const answer = adapter.usage({ key: KEY });
|
|
224
224
|
if (answer.status !== 200) fail(`usage read refused with ${answer.status}`);
|
|
@@ -246,7 +246,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
|
|
|
246
246
|
|
|
247
247
|
const check = (where) => {
|
|
248
248
|
const u = usage();
|
|
249
|
-
const bytes = u.stores.
|
|
249
|
+
const bytes = u.stores.blockeditor.bytes;
|
|
250
250
|
const shape = pageShape(page().y, limits);
|
|
251
251
|
stats.maxLoose = Math.max(stats.maxLoose, shape.loose);
|
|
252
252
|
stats.maxBytes = Math.max(stats.maxBytes, bytes);
|
|
@@ -291,12 +291,12 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
|
|
|
291
291
|
const out = adapter.fileWritten({ key: KEY, html: htmlOf(file, at) });
|
|
292
292
|
legacy.writes++;
|
|
293
293
|
const after = state();
|
|
294
|
-
if (!after.stores.
|
|
295
|
-
const y = after.stores.
|
|
294
|
+
if (!after.stores.blockeditor) fail('a merge dropped the page store');
|
|
295
|
+
const y = after.stores.blockeditor.value.y;
|
|
296
296
|
if (keysBefore.some((k) => !(k in y) || y[k] !== valueBefore.y[k])) fail('a file merge deleted or changed a room key');
|
|
297
297
|
if (!contains([update])) fail('a file merge lost the legacy edit');
|
|
298
298
|
if (out.body?.valueChanged) legacy.merged++;
|
|
299
|
-
if (out.body?.landed?.includes('
|
|
299
|
+
if (out.body?.landed?.includes('blockeditor')) stats.landed++;
|
|
300
300
|
admitted.push(update);
|
|
301
301
|
const u = usage();
|
|
302
302
|
if (u.reported !== held(u)) fail('budget after merge');
|
|
@@ -330,13 +330,13 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
|
|
|
330
330
|
const before = serializeLive(page());
|
|
331
331
|
const seqBefore = state().seq;
|
|
332
332
|
const now = state();
|
|
333
|
-
const answer = adapter.apply({ key: KEY, tabId: t.id, frame: { name: '
|
|
333
|
+
const answer = adapter.apply({ key: KEY, tabId: t.id, frame: { name: 'blockeditor', n: f.n, epoch: now.epoch, blockeditorGeneration: now.blockeditorTransition?.id ?? null, compaction: 1, ops: [{ op: 'set', path: ['y', f.key], value: f.value }] } });
|
|
334
334
|
const body = answer.body ?? {};
|
|
335
335
|
if (body.error) {
|
|
336
336
|
if (serializeLive(page()) !== before || state().seq !== seqBefore) fail(`refusal ${body.error} changed the room`);
|
|
337
337
|
stats.unchangedRefusals++;
|
|
338
338
|
if (body.error === 'checkpoint-required') { stats.refusedRetry++; return; }
|
|
339
|
-
if (body.error === '
|
|
339
|
+
if (body.error === 'blockeditor-full') { stats.capacity++; t.blocked = true; return; }
|
|
340
340
|
fail(`unexpected refusal ${body.error}`);
|
|
341
341
|
}
|
|
342
342
|
if (body.stale) { t.inflight = null; return; }
|
|
@@ -455,7 +455,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
|
|
|
455
455
|
roomDoc.destroy();
|
|
456
456
|
}
|
|
457
457
|
const end = page();
|
|
458
|
-
const out = { seed, ...stats, legacyWrites: legacy.writes, legacyMerged: legacy.merged, blocked: blocked.length, end: { bytes: usage().stores.
|
|
458
|
+
const out = { seed, ...stats, legacyWrites: legacy.writes, legacyMerged: legacy.merged, blocked: blocked.length, end: { bytes: usage().stores.blockeditor.bytes, ...pageShape(end.y, limits) } };
|
|
459
459
|
if (log) console.log(JSON.stringify(out));
|
|
460
460
|
return out;
|
|
461
461
|
}
|
|
@@ -15,8 +15,8 @@ const span = (node) => {
|
|
|
15
15
|
// Offsets only, never the parsed tree, so the remembered answers stay small.
|
|
16
16
|
const locate = recent(4, (html) => {
|
|
17
17
|
const $ = load(html, { sourceCodeLocationInfo: true });
|
|
18
|
-
const articles = $('article[data-
|
|
19
|
-
const tags = $('script[merge="
|
|
18
|
+
const articles = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
|
|
19
|
+
const tags = $('script[merge="blockeditor"]').toArray().filter((node) => !inert(node) &&
|
|
20
20
|
($(node).attr('clay') || '').split(/\s+/).includes('live'));
|
|
21
21
|
return Object.freeze({
|
|
22
22
|
articles: articles.length,
|
|
@@ -6,7 +6,7 @@ import { PageFormatError } from './page-write-guard.js';
|
|
|
6
6
|
import { ifMatchSatisfied } from './spec-wire.js';
|
|
7
7
|
import { checkInitialLineage, checkOrdinaryLineage } from './page-lineage.js';
|
|
8
8
|
|
|
9
|
-
const PAGE = /^
|
|
9
|
+
const PAGE = /^blockeditor(\.[0-9]+)?$/;
|
|
10
10
|
const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
11
11
|
const refuse = (reason) => { throw new PageConversionError(reason); };
|
|
12
12
|
|
|
@@ -26,7 +26,7 @@ function claimsYPage(html) {
|
|
|
26
26
|
function checkInitialRoom(room) {
|
|
27
27
|
if ([...(room?.stores.keys() ?? [])].some((name) => PAGE.test(name)) ||
|
|
28
28
|
[...(room?.lastN.keys() ?? [])].some((key) => PAGE.test(key.slice(0, key.indexOf('\n')))) ||
|
|
29
|
-
(room?.
|
|
29
|
+
(room?.blockeditorTransition && room.blockeditorTransition.format !== 'none')) refuse('room-blockeditor-authority');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function checkInitialCondition(ifMatch, currentHtml) {
|
|
@@ -34,18 +34,18 @@ function checkInitialCondition(ifMatch, currentHtml) {
|
|
|
34
34
|
!ifMatchSatisfied(ifMatch, currentHtml)) refuse('file-changed');
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function checkPageInitial({ currentHtml, html, ifMatch, room = null,
|
|
37
|
+
export function checkPageInitial({ currentHtml, html, ifMatch, room = null, blockeditorWrite = null, createDocument }) {
|
|
38
38
|
if (typeof currentHtml !== 'string' || inspectPage(currentHtml).kind !== 'none') refuse('invalid-source');
|
|
39
39
|
if (typeof html !== 'string' || inspectPage(html).kind !== 'yjs') refuse('invalid-capture');
|
|
40
40
|
checkInitialRoom(room);
|
|
41
41
|
checkInitialCondition(ifMatch, currentHtml);
|
|
42
|
-
checkInitialLineage({ currentHtml, html,
|
|
42
|
+
checkInitialLineage({ currentHtml, html, blockeditorWrite, createDocument });
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function checkPageWriteAdmission({ currentHtml, html, header = null, ifMatch, room = null,
|
|
45
|
+
export function checkPageWriteAdmission({ currentHtml, html, header = null, ifMatch, room = null, blockeditorWrite = null, createDocument }) {
|
|
46
46
|
const before = inspectPage(currentHtml ?? '');
|
|
47
47
|
const after = inspectPage(html ?? '');
|
|
48
|
-
const converted = before.kind === 'yjs' || room?.
|
|
48
|
+
const converted = before.kind === 'yjs' || room?.blockeditorTransition?.format === 'yjs';
|
|
49
49
|
if (converted && after.kind === 'none' && header == null) return 'ordinary';
|
|
50
50
|
if (converted && after.kind !== 'yjs') throw new PageFormatError();
|
|
51
51
|
if (header !== null && header !== undefined) {
|
|
@@ -64,6 +64,6 @@ export function checkPageWriteAdmission({ currentHtml, html, header = null, ifMa
|
|
|
64
64
|
if (before.kind !== 'none' || typeof currentHtml !== 'string') refuse('invalid-source');
|
|
65
65
|
checkInitialRoom(room);
|
|
66
66
|
checkInitialCondition(ifMatch, currentHtml);
|
|
67
|
-
checkInitialLineage({ currentHtml, html,
|
|
67
|
+
checkInitialLineage({ currentHtml, html, blockeditorWrite, createDocument });
|
|
68
68
|
return 'initial';
|
|
69
69
|
}
|
|
@@ -4,13 +4,13 @@ export class PageFormatError extends Error {
|
|
|
4
4
|
constructor() {
|
|
5
5
|
super('A converted page must retain one complete Yjs page value');
|
|
6
6
|
this.name = 'PageFormatError';
|
|
7
|
-
this.code = '
|
|
7
|
+
this.code = 'blockeditor-format';
|
|
8
8
|
this.statusCode = 409;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function checkPageWriteFormat({ currentHtml, html, room = null }) {
|
|
13
|
-
if (room?.
|
|
13
|
+
if (room?.blockeditorTransition?.format !== 'yjs' && inspectPage(currentHtml ?? '').kind !== 'yjs') return;
|
|
14
14
|
const kind = inspectPage(html ?? '').kind;
|
|
15
15
|
if (kind !== 'yjs' && kind !== 'none') throw new PageFormatError();
|
|
16
16
|
}
|
|
@@ -19,5 +19,5 @@ export function checkPageWriteFormat({ currentHtml, html, room = null }) {
|
|
|
19
19
|
// outside edit or restore replaced the article, so the room's unsaved page
|
|
20
20
|
// edits belong in a recovered copy and the room must start over.
|
|
21
21
|
export function pageLineageLost(room, html) {
|
|
22
|
-
return room?.
|
|
22
|
+
return room?.blockeditorTransition?.format === 'yjs' && room.stores.has('blockeditor') && inspectPage(html ?? '').kind === 'none';
|
|
23
23
|
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { saveToken } from "../core/host-attrs.js";
|
|
2
|
+
import { storage } from "../lib/doc-storage.js";
|
|
3
|
+
|
|
4
|
+
const TITLE = "Saved data was overwritten";
|
|
5
|
+
const DONE_KEY = "data-guard-done";
|
|
6
|
+
const READ_TIMEOUT_MS = 6000;
|
|
7
|
+
const AFTER_SAVE_MS = 1500;
|
|
8
|
+
const MIN_READ_GAP_MS = 5000;
|
|
9
|
+
const VISIBLE_GAP_MS = 30000;
|
|
10
|
+
const UNREACHED = "Could not reach the server. Try again.";
|
|
11
|
+
const REFUSED = "That did not go through. Try again.";
|
|
12
|
+
const SAVE_FIRST = "Save your changes first, then try again.";
|
|
13
|
+
const RELOAD_FIRST = "Reload the page, then try again.";
|
|
14
|
+
|
|
15
|
+
let session = null;
|
|
16
|
+
let ui = null;
|
|
17
|
+
|
|
18
|
+
const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
19
|
+
const isPending = (event) => Boolean(event) && typeof event.id === "string" && event.status === "pending";
|
|
20
|
+
const allowed = (current, event, choice, flag) => flag === true && !current.refused.has(`${event.id}:${choice}`);
|
|
21
|
+
const canRestore = (current, event) => allowed(current, event, "restore", event.restorable);
|
|
22
|
+
const canRevert = (current, event) => allowed(current, event, "revert", event.canRevert);
|
|
23
|
+
const credentials = () => (saveToken() ? "omit" : "same-origin");
|
|
24
|
+
const headers = (extra = {}) => ({ "Document-URL": location.href, ...extra });
|
|
25
|
+
|
|
26
|
+
function removeServerBar() {
|
|
27
|
+
for (const bar of document.querySelectorAll("[data-htmlclay-guard]")) bar.remove();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function say(message, type) {
|
|
31
|
+
import("../ui/toast.js").then((module) => {
|
|
32
|
+
ui = module;
|
|
33
|
+
module.default(message, type);
|
|
34
|
+
}, () => {});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function describe(current, event) {
|
|
38
|
+
const summary = event.lossSummary || {};
|
|
39
|
+
const source = summary.provenance === "external" ? "A change from outside this page" : "A save from this page";
|
|
40
|
+
const loss = summary.removedItems > 0
|
|
41
|
+
? `removed ${plural(summary.removedItems, "item")}`
|
|
42
|
+
: summary.destroyedAtoms > 0 ? `erased ${plural(summary.destroyedAtoms, "value")}` : "changed saved data";
|
|
43
|
+
const row = Array.isArray(event.preview) ? event.preview[0] : null;
|
|
44
|
+
const parts = [`${source} ${loss}.`];
|
|
45
|
+
if (row) parts.push(`${row.key}: ${row.yours} before, ${row.now} now.`);
|
|
46
|
+
if (canRestore(current, event)) {
|
|
47
|
+
if (event.droppedAdditions > 0) parts.push(`Restoring removes ${plural(event.droppedAdditions, "item")} added or changed since.`);
|
|
48
|
+
} else if (canRevert(current, event)) {
|
|
49
|
+
parts.push("This data no longer fits the page, but the whole change can be undone.");
|
|
50
|
+
} else {
|
|
51
|
+
parts.push("It cannot be restored from here.");
|
|
52
|
+
}
|
|
53
|
+
if (canRevert(current, event)) parts.push("Undo the change also removes edits made since.");
|
|
54
|
+
return parts.join(" ");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function actionsFor(current, event) {
|
|
58
|
+
const actions = [];
|
|
59
|
+
if (canRestore(current, event)) actions.push({ label: "Restore", onClick: () => choose(current, event, "restore") });
|
|
60
|
+
if (canRevert(current, event)) actions.push({ label: "Undo the change", onClick: () => choose(current, event, "revert") });
|
|
61
|
+
actions.push({ label: "Keep as is", onClick: () => choose(current, event, "dismiss") });
|
|
62
|
+
return actions;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function draw(current, event) {
|
|
66
|
+
const key = [event.id, canRestore(current, event), canRevert(current, event), event.lastWriteAt, describe(current, event)].join("|");
|
|
67
|
+
if (current.drawn === key) return true;
|
|
68
|
+
current.drawn = key;
|
|
69
|
+
current.eventId = event.id;
|
|
70
|
+
try {
|
|
71
|
+
ui ??= await import("../ui/toast.js");
|
|
72
|
+
} catch {
|
|
73
|
+
if (current.drawn === key) {
|
|
74
|
+
current.drawn = null;
|
|
75
|
+
current.eventId = null;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (session !== current || current.drawn !== key) return false;
|
|
80
|
+
ui.dismissPersistent(TITLE, { now: true });
|
|
81
|
+
ui.toastPersistent(TITLE, "warning", { detail: describe(current, event), actions: actionsFor(current, event) });
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function clearNotice(current) {
|
|
86
|
+
current.drawn = null;
|
|
87
|
+
current.eventId = null;
|
|
88
|
+
ui?.dismissPersistent(TITLE, { now: true });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function read(current) {
|
|
92
|
+
const turn = ++current.turn;
|
|
93
|
+
current.lastRead = Date.now();
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
const timer = setTimeout(() => controller.abort(), READ_TIMEOUT_MS);
|
|
96
|
+
let body;
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch(current.url, {
|
|
99
|
+
method: "GET",
|
|
100
|
+
credentials: credentials(),
|
|
101
|
+
headers: headers(),
|
|
102
|
+
cache: "no-store",
|
|
103
|
+
redirect: "manual",
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
});
|
|
106
|
+
if (!res.ok) return;
|
|
107
|
+
body = await res.json();
|
|
108
|
+
} catch {
|
|
109
|
+
return;
|
|
110
|
+
} finally {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
if (session !== current || turn !== current.turn) return;
|
|
114
|
+
if (!body || typeof body !== "object" || !("event" in body)) return;
|
|
115
|
+
if (isPending(body.event)) {
|
|
116
|
+
if (current.resolved.has(body.event.id)) return;
|
|
117
|
+
if (await draw(current, body.event)) removeServerBar();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
clearNotice(current);
|
|
121
|
+
removeServerBar();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function forget(current, event) {
|
|
125
|
+
current.resolved.add(event.id);
|
|
126
|
+
if (current.eventId === event.id) clearNotice(current);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function finish(current, event, choice) {
|
|
130
|
+
forget(current, event);
|
|
131
|
+
if (choice === "dismiss") {
|
|
132
|
+
say("Kept the page as it is.", "info");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
storage.session.set(DONE_KEY, choice);
|
|
136
|
+
current.reload();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function settle(current, event, choice, status, body) {
|
|
140
|
+
if (session !== current) return;
|
|
141
|
+
if (status === 200 && body?.ok === true) {
|
|
142
|
+
finish(current, event, choice);
|
|
143
|
+
} else if (status === 404) {
|
|
144
|
+
forget(current, event);
|
|
145
|
+
say("This was already taken care of somewhere else. Reload to see the current page.", "info");
|
|
146
|
+
} else if (status === 409) {
|
|
147
|
+
current.drawn = null;
|
|
148
|
+
say("This page changed after the notice appeared. Check the notice and try again.", "warning");
|
|
149
|
+
read(current);
|
|
150
|
+
} else if (status === 422) {
|
|
151
|
+
current.refused.add(`${event.id}:${choice}`);
|
|
152
|
+
if (current.eventId === event.id && !current.resolved.has(event.id)) draw(current, event);
|
|
153
|
+
say(choice === "restore" ? "That data no longer fits this page, so it could not be restored." : "That earlier version is no longer available.", "warning");
|
|
154
|
+
} else if (status === 401 || status === 403) {
|
|
155
|
+
say(RELOAD_FIRST, "error");
|
|
156
|
+
} else {
|
|
157
|
+
say(status === 400 ? REFUSED : UNREACHED, "error");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function saveFirst() {
|
|
162
|
+
const clay = window.clay;
|
|
163
|
+
if (!clay?.hasUnsavedChanges?.()) return true;
|
|
164
|
+
if (typeof clay.save?.flush !== "function") return false;
|
|
165
|
+
try {
|
|
166
|
+
await clay.save.flush();
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
return !clay.hasUnsavedChanges();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function choose(current, event, choice) {
|
|
174
|
+
if (current.busy || session !== current) return;
|
|
175
|
+
current.busy = true;
|
|
176
|
+
try {
|
|
177
|
+
if (choice !== "dismiss" && !(await saveFirst())) {
|
|
178
|
+
if (session === current) say(SAVE_FIRST, "warning");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (session !== current) return;
|
|
182
|
+
const res = await fetch(current.url, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
credentials: credentials(),
|
|
185
|
+
headers: headers({ "Content-Type": "application/json" }),
|
|
186
|
+
redirect: "manual",
|
|
187
|
+
body: JSON.stringify({ id: event.id, choice }),
|
|
188
|
+
});
|
|
189
|
+
const body = await res.json().catch(() => null);
|
|
190
|
+
settle(current, event, choice, res.status, body);
|
|
191
|
+
} catch {
|
|
192
|
+
if (session === current) say(UNREACHED, "error");
|
|
193
|
+
} finally {
|
|
194
|
+
current.busy = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function onNotification(current, e) {
|
|
199
|
+
const { msgType, action, data } = e.detail || {};
|
|
200
|
+
if (msgType !== "data-loss") return;
|
|
201
|
+
if (action === "resolved") {
|
|
202
|
+
if (typeof data?.id !== "string") return;
|
|
203
|
+
current.turn++;
|
|
204
|
+
current.resolved.add(data.id);
|
|
205
|
+
if (current.eventId === data.id) clearNotice(current);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (action === "raised" && isPending(data) && !current.resolved.has(data.id)) {
|
|
209
|
+
current.turn++;
|
|
210
|
+
draw(current, data).then((drawn) => { if (drawn) removeServerBar(); }, () => {});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const readAt = (current) => Math.max(current.lastSave + AFTER_SAVE_MS, current.lastRead + MIN_READ_GAP_MS);
|
|
215
|
+
|
|
216
|
+
function armRead(current) {
|
|
217
|
+
if (current.timer) return;
|
|
218
|
+
current.timer = setTimeout(() => {
|
|
219
|
+
current.timer = null;
|
|
220
|
+
if (session !== current) return;
|
|
221
|
+
if (readAt(current) > Date.now()) armRead(current);
|
|
222
|
+
else read(current);
|
|
223
|
+
}, Math.max(readAt(current) - Date.now(), 0));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function start({ href, reload = () => window.location.reload() }) {
|
|
227
|
+
if (session) return;
|
|
228
|
+
let url;
|
|
229
|
+
try {
|
|
230
|
+
url = new URL(href, location.href);
|
|
231
|
+
} catch {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (url.origin !== location.origin) return;
|
|
235
|
+
const listeners = new AbortController();
|
|
236
|
+
const current = {
|
|
237
|
+
url: url.href, reload, listeners, turn: 0, lastRead: 0, lastSave: 0, timer: null,
|
|
238
|
+
busy: false, drawn: null, eventId: null, resolved: new Set(), refused: new Set(),
|
|
239
|
+
};
|
|
240
|
+
session = current;
|
|
241
|
+
const { signal } = listeners;
|
|
242
|
+
document.addEventListener("clay:notification", (e) => onNotification(current, e), { signal });
|
|
243
|
+
document.addEventListener("clay:save-saved", () => {
|
|
244
|
+
current.lastSave = Date.now();
|
|
245
|
+
armRead(current);
|
|
246
|
+
}, { signal });
|
|
247
|
+
document.addEventListener("visibilitychange", () => {
|
|
248
|
+
if (document.visibilityState === "visible" && Date.now() - current.lastRead >= VISIBLE_GAP_MS) read(current);
|
|
249
|
+
}, { signal });
|
|
250
|
+
const done = storage.session.get(DONE_KEY);
|
|
251
|
+
if (done) {
|
|
252
|
+
storage.session.remove(DONE_KEY);
|
|
253
|
+
say(done === "restore" ? "Your saved data is back." : "The earlier version is back.", "success");
|
|
254
|
+
}
|
|
255
|
+
read(current);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function stop() {
|
|
259
|
+
const current = session;
|
|
260
|
+
if (!current) return;
|
|
261
|
+
session = null;
|
|
262
|
+
current.listeners.abort();
|
|
263
|
+
clearTimeout(current.timer);
|
|
264
|
+
clearNotice(current);
|
|
265
|
+
}
|
package/src/sync/live-sync.js
CHANGED
|
@@ -48,13 +48,13 @@ import { mergeIgnoresAttr, mergeVetoesAttr } from '../lib/root-attrs.js';
|
|
|
48
48
|
import { autosaveActive } from '../lib/autosave-state.js';
|
|
49
49
|
import { hostSupports } from '../core/host-meta.js';
|
|
50
50
|
import { PAGE_FORMAT, PAGE_FORMAT_HEADER } from '../core/page-format-wire.js';
|
|
51
|
-
import { knownPageFormatFloor,
|
|
51
|
+
import { knownPageFormatFloor, blockeditorFormatFloor, withPageFormatFloor } from '../core/page-format.js';
|
|
52
52
|
import { onSaveAccepted } from '../core/save-accepted.js';
|
|
53
53
|
import { servedDocumentEtag } from '../core/host-attrs.js';
|
|
54
54
|
import { recordEtag, seedEtag, lastSeenEtag, conditionalSaves, representedEtag, forgetRepresentedEtag } from '../core/etag.js';
|
|
55
55
|
import { pageMaybeDirty, pauseGate, resumeGate, gateCaptureToken, gateClearIfUnchanged, gateMarkDirty } from '../lib/dirty-gate.js';
|
|
56
56
|
import { gestureSeen } from '../lib/user-gesture.js';
|
|
57
|
-
import { SyncStream } from './stream.js';
|
|
57
|
+
import { SyncStream, pageHidden } from './stream.js';
|
|
58
58
|
import { streamOpened, streamClosed, streamEvent, LIVE_EVENTS } from '../live/stream-bridge.js';
|
|
59
59
|
import { conflicts, conflictLedger, loadConflicts } from './conflicts-facade.js';
|
|
60
60
|
import { upgradeReplaces, stopForUpgrade } from './version-guard.js';
|
|
@@ -364,6 +364,7 @@ class LiveSync {
|
|
|
364
364
|
this._profilePromise = null;
|
|
365
365
|
this._sharedData = false;
|
|
366
366
|
this._sharedSync = false;
|
|
367
|
+
this._workerFeatures = ['live-data'];
|
|
367
368
|
this._liveData = false;
|
|
368
369
|
this._editing = null;
|
|
369
370
|
this._presenceLease = null;
|
|
@@ -390,6 +391,7 @@ class LiveSync {
|
|
|
390
391
|
|
|
391
392
|
// Store handler reference for cleanup
|
|
392
393
|
this._snapshotHandler = null;
|
|
394
|
+
this._hiddenHandler = null;
|
|
393
395
|
|
|
394
396
|
// High-water mark of server seqs we've seen on this channel (own echoes
|
|
395
397
|
// count too). Server-broadcast payloads carry a monotonic seq
|
|
@@ -776,6 +778,12 @@ class LiveSync {
|
|
|
776
778
|
this._settledHandler = null;
|
|
777
779
|
}
|
|
778
780
|
|
|
781
|
+
if (this._hiddenHandler) {
|
|
782
|
+
document.removeEventListener('visibilitychange', this._hiddenHandler);
|
|
783
|
+
document.removeEventListener('clay:workspace-background', this._hiddenHandler);
|
|
784
|
+
this._hiddenHandler = null;
|
|
785
|
+
}
|
|
786
|
+
|
|
779
787
|
this._queuedSend = null;
|
|
780
788
|
|
|
781
789
|
// Cancel any pending frame and clear the queue. A morph already in
|
|
@@ -848,6 +856,7 @@ class LiveSync {
|
|
|
848
856
|
this._sharedData = resolved.sharedData;
|
|
849
857
|
this._sharedSync = resolved.sharedSync;
|
|
850
858
|
this._liveData = resolved.liveData;
|
|
859
|
+
this._workerFeatures = resolved.workerFeatures;
|
|
851
860
|
this._profile = resolved.profile;
|
|
852
861
|
this._log(`Wire profile: ${resolved.profile.name}`);
|
|
853
862
|
return resolved.profile;
|
|
@@ -925,15 +934,33 @@ class LiveSync {
|
|
|
925
934
|
// profile is known, so this is never null on the normal path.
|
|
926
935
|
const profile = this._profile || WIRE_PROFILES.legacy;
|
|
927
936
|
const path = profile.streamPath(window.location.href, this.lane, this.resumeId, this.clientId);
|
|
937
|
+
// A frame scheduled while the page showed waits for an animation frame, which a
|
|
938
|
+
// page hidden since then never runs. Move it to the hidden schedule.
|
|
939
|
+
if (!this._hiddenHandler) {
|
|
940
|
+
this._hiddenHandler = () => {
|
|
941
|
+
if (this._rafHandle == null || !pageHidden()) return;
|
|
942
|
+
this._cancelFrame(this._rafHandle);
|
|
943
|
+
this._rafHandle = null;
|
|
944
|
+
this._scheduleNextFrame();
|
|
945
|
+
};
|
|
946
|
+
document.addEventListener('visibilitychange', this._hiddenHandler);
|
|
947
|
+
document.addEventListener('clay:workspace-background', this._hiddenHandler);
|
|
948
|
+
}
|
|
928
949
|
// Resolved against the real origin: a <base href> in the authored document
|
|
929
950
|
// would otherwise point the sync stream at an origin the document chose.
|
|
930
951
|
this.sse = new SyncStream(new URL(path, window.location.origin).href, {
|
|
931
952
|
shared: this._sharedSync,
|
|
932
953
|
sharedData: this._sharedData,
|
|
954
|
+
workerFeatures: this._workerFeatures,
|
|
933
955
|
documentURL: window.location.href,
|
|
934
956
|
lane: this.lane,
|
|
935
957
|
clientId: this.clientId,
|
|
936
|
-
|
|
958
|
+
// A hidden edit tab stays subscribed wherever that costs no connection of its
|
|
959
|
+
// own: a live-data host keeps it for the room, and the shared worker carries it
|
|
960
|
+
// for free ('shared': the stream decides, from the connection it holds).
|
|
961
|
+
// Released, it would miss a peer's save, and its own next save would be
|
|
962
|
+
// refused as a conflict.
|
|
963
|
+
keepAliveWhenHidden: this.lane !== 'live' ? false : (this._liveData ? true : 'shared'),
|
|
937
964
|
// Only a response that named its own version gives this tab anything to
|
|
938
965
|
// compare against. On a host that served none there is no provenance to
|
|
939
966
|
// check, and the page keeps its pre-1.9.0 startup behavior.
|
|
@@ -1305,7 +1332,7 @@ class LiveSync {
|
|
|
1305
1332
|
// at: the save adds the format marker on its way out (core/save-core.js runSave),
|
|
1306
1333
|
// after the capture this relay was built from. At floor 0 the html is unchanged.
|
|
1307
1334
|
const sent = acceptedBytesFor(captureId) ?? acceptedHtml;
|
|
1308
|
-
const floor = typeof sent === 'string' ?
|
|
1335
|
+
const floor = typeof sent === 'string' ? blockeditorFormatFloor(sent) : 0;
|
|
1309
1336
|
payload = { ...payload, html: asSent(withPageFormatFloor(payload.html, Math.max(knownPageFormatFloor(), floor)), sent) };
|
|
1310
1337
|
|
|
1311
1338
|
// Skip only an unstamped repeat, judged against the base as it stood. A
|
|
@@ -1919,19 +1946,35 @@ class LiveSync {
|
|
|
1919
1946
|
return true;
|
|
1920
1947
|
}
|
|
1921
1948
|
|
|
1949
|
+
// A hidden page runs no animation frames, and Chrome slows its timers to one a
|
|
1950
|
+
// second (one a minute after five minutes). A frame left waiting there kept the
|
|
1951
|
+
// stamp of the version before a peer's save, so the tab's next save was refused
|
|
1952
|
+
// as a conflict. A message the page posts to itself runs at once, hidden or not.
|
|
1922
1953
|
_requestFrame(cb) {
|
|
1954
|
+
if (pageHidden()) {
|
|
1955
|
+
if (typeof MessageChannel === 'function') {
|
|
1956
|
+
const channel = new MessageChannel();
|
|
1957
|
+
let live = true;
|
|
1958
|
+
channel.port1.onmessage = () => {
|
|
1959
|
+
channel.port1.close();
|
|
1960
|
+
if (live) cb();
|
|
1961
|
+
};
|
|
1962
|
+
channel.port2.postMessage(null);
|
|
1963
|
+
return { cancel: () => { live = false; channel.port1.close(); } };
|
|
1964
|
+
}
|
|
1965
|
+
const timer = setTimeout(cb, 0);
|
|
1966
|
+
return { cancel: () => clearTimeout(timer) };
|
|
1967
|
+
}
|
|
1923
1968
|
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
|
|
1924
|
-
|
|
1969
|
+
const frame = window.requestAnimationFrame(cb);
|
|
1970
|
+
return { cancel: () => window.cancelAnimationFrame?.(frame) };
|
|
1925
1971
|
}
|
|
1926
|
-
|
|
1972
|
+
const timer = setTimeout(cb, 16);
|
|
1973
|
+
return { cancel: () => clearTimeout(timer) };
|
|
1927
1974
|
}
|
|
1928
1975
|
|
|
1929
1976
|
_cancelFrame(handle) {
|
|
1930
|
-
|
|
1931
|
-
window.cancelAnimationFrame(handle);
|
|
1932
|
-
return;
|
|
1933
|
-
}
|
|
1934
|
-
clearTimeout(handle);
|
|
1977
|
+
handle?.cancel?.();
|
|
1935
1978
|
}
|
|
1936
1979
|
|
|
1937
1980
|
/**
|