@panphora/clayjs 1.15.2 → 1.16.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/README.md +4 -5
- package/THIRD-PARTY-NOTICES.md +16 -0
- package/compat/highlight-11.9.0.min.js +1213 -0
- package/compat/highlight-LICENSE +29 -0
- package/compat/journal-assets.lock.json +22 -0
- package/compat/overtype-2.6.1.min.js +116 -0
- package/compat/overtype-LICENSE +21 -0
- package/dist/all.js +90 -0
- package/dist/clay-dom.js +84 -0
- package/dist/clay-events.js +103 -0
- package/dist/clay-internals.js +85 -0
- package/dist/clay-options.js +93 -0
- package/dist/clay-router.js +98 -0
- package/dist/clay-ui.js +95 -0
- package/dist/clay-utils.js +84 -0
- package/dist/clay.js +146 -0
- package/dist/clay.standalone.js +83090 -58706
- package/dist/m/attrs/add-from-T5LJXFFI.js +1 -0
- package/dist/m/attrs/onaftersave-OKSWEJQF.js +1 -0
- package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +1 -0
- package/dist/m/attrs/save-freeze-FUP4Q4X3.js +1 -0
- package/dist/m/c-2AZDZWCK.js +1 -0
- package/dist/m/c-2JL6E75N.js +1 -0
- package/dist/m/c-2M73252O.js +1 -0
- package/dist/m/c-2SPVQSHC.js +1 -0
- package/dist/m/c-33HUYEDI.js +1 -0
- package/dist/m/c-33JJ6CBX.js +2 -0
- package/dist/m/c-35NBGMZB.js +1 -0
- package/dist/m/c-3GJ4SDE2.js +1 -0
- package/dist/m/c-3IQRECYS.js +1 -0
- package/dist/m/c-3OWDMNJR.js +2 -0
- package/dist/m/c-46XYM55L.js +1 -0
- package/dist/m/c-4B6H4G5V.js +1 -0
- package/dist/m/c-4DRMS4QG.js +9 -0
- package/dist/m/c-4FFHZM32.js +1 -0
- package/dist/m/c-4JZDZFF6.js +1 -0
- package/dist/m/c-4L3BTFRK.js +1 -0
- package/dist/m/c-4Q2XTQ2N.js +2 -0
- package/dist/m/c-4WKFKEKF.js +1 -0
- package/dist/m/c-4YSCRICO.js +1 -0
- package/dist/m/c-54RCDCBU.js +1 -0
- package/dist/m/c-5M4YEKCX.js +4 -0
- package/dist/m/c-5R4PTRG2.js +1 -0
- package/dist/m/c-5V3MOVJT.js +1 -0
- package/dist/m/c-5VYOWDTI.js +1 -0
- package/dist/m/c-5Y6TNX5W.js +34 -0
- package/dist/m/c-5YIHJ3TJ.js +1 -0
- package/dist/m/c-66O32MV3.js +28 -0
- package/dist/m/c-6HMB24EV.js +1 -0
- package/dist/m/c-7DJDKPGL.js +1 -0
- package/dist/m/c-7O6AFR65.js +1 -0
- package/dist/m/c-7SWMYKAI.js +1 -0
- package/dist/m/c-7VGZ7RU5.js +1 -0
- package/dist/m/c-A7LS5UZW.js +1 -0
- package/dist/m/c-ANQTJ3B5.js +2 -0
- package/dist/m/c-AQLFEATB.js +1 -0
- package/dist/m/c-AYVOREBE.js +1 -0
- package/dist/m/c-B2ZXPOS3.js +3 -0
- package/dist/m/c-BBVGTQG7.js +1 -0
- package/dist/m/c-BMIZKMPV.js +1 -0
- package/dist/m/c-BTDI4Y23.js +1 -0
- package/dist/m/c-BWFVSBDF.js +1 -0
- package/dist/m/c-BYTKL6UX.js +1 -0
- package/dist/m/c-CFHW5AMY.js +6 -0
- package/dist/m/c-CJZSLMVQ.js +1 -0
- package/dist/m/c-CYWLPPUS.js +1 -0
- package/dist/m/c-CZUSOB7V.js +1 -0
- package/dist/m/c-DBE6ORSI.js +1 -0
- package/dist/m/c-DCFSEJZG.js +1 -0
- package/dist/m/c-DNZ5IFM6.js +1 -0
- package/dist/m/c-EDHUR4MX.js +1 -0
- package/dist/m/c-EJTGYSLN.js +1 -0
- package/dist/m/c-EY6UOM54.js +1 -0
- package/dist/m/c-F3CKJTJS.js +1 -0
- package/dist/m/c-F7Y6FLPY.js +1 -0
- package/dist/m/c-FA4DZVTC.js +1 -0
- package/dist/m/c-FILLTJA7.js +1 -0
- package/dist/m/c-G2BUZITR.js +1 -0
- package/dist/m/c-GB3JTV6E.js +1 -0
- package/dist/m/c-GBGWXTX4.js +1 -0
- package/dist/m/c-GHBCCVG4.js +1 -0
- package/dist/m/c-GJAEGGXY.js +1 -0
- package/dist/m/c-GOWBDM6J.js +1 -0
- package/dist/m/c-GQ6F6IDK.js +1 -0
- package/dist/m/c-H4LG442Q.js +1 -0
- package/dist/m/c-HA6INT6J.js +1 -0
- package/dist/m/c-HPURYVZS.js +2 -0
- package/dist/m/c-HRDCD3WN.js +67 -0
- package/dist/m/c-HT33EQRN.js +1 -0
- package/dist/m/c-HVYXIS72.js +1 -0
- package/dist/m/c-IB52R237.js +1 -0
- package/dist/m/c-IJUK2GPW.js +1 -0
- package/dist/m/c-IKWNE4R4.js +1 -0
- package/dist/m/c-IU3IYEU6.js +1 -0
- package/dist/m/c-IZ2EBRAJ.js +1 -0
- package/dist/m/c-J7CA54IP.js +1 -0
- package/dist/m/c-JAX2667S.js +6 -0
- package/dist/m/c-KI7M4AY3.js +1 -0
- package/dist/m/c-KIPQJX4B.js +1 -0
- package/dist/m/c-KKWLDOPM.js +1 -0
- package/dist/m/c-KR4UW4KW.js +1 -0
- package/dist/m/c-KY3VJGUA.js +8 -0
- package/dist/m/c-L4WQSC27.js +1 -0
- package/dist/m/c-LFPHCXY3.js +1 -0
- package/dist/m/c-LR4CGEPT.js +91 -0
- package/dist/m/c-LRPUO2MX.js +1 -0
- package/dist/m/c-LTGMXXKO.js +1 -0
- package/dist/m/c-LXENEBV7.js +1 -0
- package/dist/m/c-LZJPNR2W.js +1 -0
- package/dist/m/c-MMUIUUZG.js +2 -0
- package/dist/m/c-MTQHWQC3.js +1 -0
- package/dist/m/c-N326R2JD.js +1 -0
- package/dist/m/c-N7JPUPGC.js +1 -0
- package/dist/m/c-NMJRXKPC.js +8 -0
- package/dist/m/c-NRPJEVJS.js +1 -0
- package/dist/m/c-NUHP2XDB.js +1 -0
- package/dist/m/c-NZMNXVF6.js +1 -0
- package/dist/m/c-OCSZ4QGN.js +1 -0
- package/dist/m/c-OIBUTDXI.js +1 -0
- package/dist/m/c-OIDRZKAF.js +1 -0
- package/dist/m/c-OYTFP5QN.js +1 -0
- package/dist/m/c-P7KNDG34.js +1 -0
- package/dist/m/c-PPOSCAQD.js +1 -0
- package/dist/m/c-PQ5LK6QE.js +993 -0
- package/dist/m/c-PZ4XEPUP.js +2 -0
- package/dist/m/c-QHIWXHRO.js +1 -0
- package/dist/m/c-QHWJ5ZIJ.js +1 -0
- package/dist/m/c-QIX5BJYN.js +1 -0
- package/dist/m/c-QNKREOL6.js +1 -0
- package/dist/m/c-QQHEX6HA.js +1 -0
- package/dist/m/c-R7RMANJI.js +9 -0
- package/dist/m/c-RI4NT75Y.js +1 -0
- package/dist/m/c-RM7J7BV2.js +1 -0
- package/dist/m/c-RUJKSUQW.js +1 -0
- package/dist/m/c-RVMWGXEJ.js +1 -0
- package/dist/m/c-S7H2DRCB.js +1 -0
- package/dist/m/c-S7LK4KUE.js +1 -0
- package/dist/m/c-SGCCC4FW.js +25 -0
- package/dist/m/c-SGIA4IEQ.js +188 -0
- package/dist/m/c-SWRJFCNN.js +1 -0
- package/dist/m/c-T4VO4OCB.js +1 -0
- package/dist/m/c-TCCA5BZN.js +2 -0
- package/dist/m/c-TQEMVJCS.js +1 -0
- package/dist/m/c-TQZ2JYHD.js +1 -0
- package/dist/m/c-TR6RMDQ7.js +3 -0
- package/dist/m/c-TRS6TM6O.js +1 -0
- package/dist/m/c-UGHH4MB3.js +1 -0
- package/dist/m/c-UKHTLWUW.js +1 -0
- package/dist/m/c-UL2YUY52.js +1 -0
- package/dist/m/c-UNN7LWCT.js +1 -0
- package/dist/m/c-UXBMRDZB.js +235 -0
- package/dist/m/c-V7PZZRG7.js +1 -0
- package/dist/m/c-VAK7SG37.js +1 -0
- package/dist/m/c-VDSS4K3Z.js +1 -0
- package/dist/m/c-VFX45IZQ.js +1 -0
- package/dist/m/c-VHNSBD2M.js +1 -0
- package/dist/m/c-VKEDWYDF.js +1 -0
- package/dist/m/c-VL52ADDR.js +1 -0
- package/dist/m/c-WN5AY3OZ.js +1 -0
- package/dist/m/c-WNYBLIOZ.js +1 -0
- package/dist/m/c-WRBKEJD4.js +1 -0
- package/dist/m/c-WWDBYXLK.js +1 -0
- package/dist/m/c-WYFDLITL.js +1 -0
- package/dist/m/c-XN6MBKI6.js +67 -0
- package/dist/m/c-XNNW2CWK.js +1 -0
- package/dist/m/c-XPELB6E5.js +1 -0
- package/dist/m/c-XQXZAO7N.js +1 -0
- package/dist/m/c-Y6USGTS5.js +5 -0
- package/dist/m/c-YDH7Q3NY.js +1 -0
- package/dist/m/c-YG2NJYS6.js +135 -0
- package/dist/m/c-Z36J63PP.js +1 -0
- package/dist/m/c-Z4VRC7GU.js +7 -0
- package/dist/m/c-Z6K52OLP.js +19 -0
- package/dist/m/c-ZDY2MDAA.js +21 -0
- package/dist/m/c-ZE46QDIH.js +1 -0
- package/dist/m/c-ZUCOKOHT.js +1 -0
- package/dist/m/core/admin-attrs-VGNLAUDK.js +1 -0
- package/dist/m/core/autosave-E4PEFPZ6.js +1 -0
- package/dist/m/core/conflict-notice-VQ6Y64PH.js +1 -0
- package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
- package/dist/m/core/host-permission-XN3K2IW3.js +1 -0
- package/dist/m/core/is-edit-mode-JXHDVGUL.js +1 -0
- package/dist/m/core/persist-HKHA66IY.js +3 -0
- package/dist/m/core/recovery-TBBSICE2.js +1 -0
- package/dist/m/core/save-WUCHTQBE.js +1 -0
- package/dist/m/core/save-core-FJVHT5M3.js +1 -0
- package/dist/m/core/snapshot-F3SWDZMM.js +1 -0
- package/dist/m/core/unsaved-warning-STKBHE6G.js +1 -0
- package/dist/m/dom/all-APS5ZSKP.js +1 -0
- package/dist/m/dom/dom-helpers-L7OSR6S4.js +1 -0
- package/dist/m/events/index-23DPTUT4.js +1 -0
- package/dist/m/internals/index-WOVAP3RU.js +1 -0
- package/dist/m/lib/cache-bust-CGWT6ZJI.js +1 -0
- package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
- package/dist/m/loader-HXX6YNH5.js +1 -0
- package/dist/m/manifest-F22DEDAC.js +1 -0
- package/dist/m/manifest-edit-C3C7376B.js +1 -0
- package/dist/m/manifest.json +3491 -0
- package/dist/m/options/options-2LH76JQ3.js +1 -0
- package/dist/m/page/editor-XUTB2HB2.js +189 -0
- package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +6 -0
- package/dist/m/plugins/bind-text-NX46N5UV.js +1 -0
- package/dist/m/plugins/cms-HP3LSTPN.js +2293 -0
- package/dist/m/plugins/demo-ZIBFAA3R.js +1 -0
- package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
- package/dist/m/plugins/indicator-PV4YW2ID.js +1 -0
- package/dist/m/plugins/live-NUTTXSBS.js +1 -0
- package/dist/m/plugins/menu-HCKO3DSP.js +138 -0
- package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
- package/dist/m/plugins/page-KSUH7G6J.js +2 -0
- package/dist/m/plugins/page-view-W73OW7WI.js +1 -0
- package/dist/m/plugins/people-AJJ32YCG.js +1 -0
- package/dist/m/plugins/quickcrop-QNY3PKLD.js +26 -0
- package/dist/m/plugins/quickcrop-QNY3PKLD.js.LEGAL.txt +7 -0
- package/dist/m/plugins/richclay-RC3L2F6X.js +517 -0
- package/dist/m/plugins/richclay-RC3L2F6X.js.LEGAL.txt +1 -0
- package/dist/m/plugins/sortable-YRMQX3ZU.js +1 -0
- package/dist/m/plugins/source-KHK5UUSK.js +1 -0
- package/dist/m/plugins/undo-OF2L3CXU.js +1 -0
- package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
- package/dist/m/plugins/upgrade-ZNYODP5Y.js +1 -0
- package/dist/m/plugins/upload-XSGNILUY.js +1 -0
- package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +21 -0
- package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +1 -0
- package/dist/m/plugins/wire-ZWVWKF76.js +1 -0
- package/dist/m/reader-core-ILWLXVMP.js +68 -0
- package/dist/m/route/route-KOCZSEGN.js +1 -0
- package/dist/m/sync/live-sync-4AUVXVJU.js +1 -0
- package/dist/m/ui/index-EVHKMKBL.js +1 -0
- package/dist/m/utils/index-4TJCVLT7.js +1 -0
- package/dist/m/vendor/Sortable.vendor-A755ITLN.js +3 -0
- package/dist/m/vendor/Sortable.vendor-A755ITLN.js.LEGAL.txt +1 -0
- package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +6 -0
- package/dist/offline-manifest.json +3800 -0
- package/dist/page/themes/light-1.css +406 -0
- package/dist/server/core.cjs +23662 -22860
- package/dist/server/offline-runtime.cjs +4954 -0
- package/dist/server/offline-runtime.mjs +4923 -0
- package/dist/server/rooms.mjs +9 -0
- package/docs/guide.md +1078 -669
- package/entries/all.js +2 -0
- package/entries/clay-data.js +1 -1
- package/entries/clay-dom.js +2 -0
- package/entries/clay-events.js +2 -0
- package/entries/clay-internals.js +2 -0
- package/entries/clay-options.js +2 -0
- package/entries/clay-router.js +2 -0
- package/entries/clay-ui.js +2 -0
- package/entries/clay-utils.js +2 -0
- package/entries/clay.js +2 -0
- package/entries/sap.js +6 -6
- package/fonts/ibm-plex-mono-LICENSE +93 -0
- package/fonts/ibm-plex-mono-latin-300-normal.woff2 +0 -0
- package/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
- package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- package/fonts/ibm-plex-sans-LICENSE +93 -0
- package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/fonts/ibm-plex.css +71 -0
- package/fonts/inter-LICENSE +93 -0
- package/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
- package/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
- package/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
- package/fonts/inter-greek-wght-normal.woff2 +0 -0
- package/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
- package/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
- package/fonts/inter.css +62 -0
- package/fonts/newsreader-LICENSE +93 -0
- package/fonts/newsreader-latin-ext-opsz-normal.woff2 +0 -0
- package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
- package/fonts/newsreader-vietnamese-opsz-normal.woff2 +0 -0
- package/fonts/newsreader.css +26 -0
- package/fonts/work-sans-LICENSE +93 -0
- package/fonts/work-sans-latin-wght-normal.woff2 +0 -0
- package/fonts/work-sans.css +8 -0
- package/package.json +20 -3
- package/packed-contract.json +1 -0
- package/src/attrs/add-from.js +27 -4
- package/src/attrs/save-freeze.js +7 -52
- package/src/core/admin-attrs.js +119 -12
- package/src/core/admin-contenteditable.js +12 -10
- package/src/core/admin-inputs.js +14 -15
- package/src/core/admin-onclick.js +2 -0
- package/src/core/autosave.js +74 -21
- package/src/core/conflict-download.js +22 -0
- package/src/core/conflict-notice-ui.js +1173 -0
- package/src/core/conflict-notice.js +140 -697
- package/src/core/edit-mode.js +147 -14
- package/src/core/etag.js +30 -12
- package/src/core/host-meta.js +72 -8
- package/src/core/host-permission.js +29 -0
- package/src/core/is-edit-mode.js +145 -12
- package/src/core/mode-key.js +125 -0
- package/src/core/mode-lifecycle.js +544 -0
- package/src/core/mode-receipt.js +30 -0
- package/src/core/mode-refusal.js +13 -0
- package/src/core/mode-resources.js +52 -0
- package/src/core/mode-support.js +82 -0
- package/src/core/mode-switch.js +259 -0
- package/src/core/mode-transition.js +112 -0
- package/src/core/page-data-write.js +305 -0
- package/src/core/page-data.js +26 -227
- package/src/core/page-format-wire.js +4 -0
- package/src/core/page-format.js +58 -0
- package/src/core/persist.js +37 -9
- package/src/core/preserve-authored.js +114 -0
- package/src/core/recovery.js +772 -0
- package/src/core/runtime-url.js +105 -0
- package/src/core/save-core.js +109 -12
- package/src/core/save-replace.js +72 -0
- package/src/core/save-size-notice.js +134 -0
- package/src/core/save-size.js +140 -0
- package/src/core/save.js +805 -118
- package/src/core/snapshot.js +121 -11
- package/src/core/source-map.js +157 -9
- package/src/core/unsaved-warning.js +14 -4
- package/src/core/upgrade-run.js +408 -0
- package/src/core/version.js +18 -0
- package/src/lib/assets.js +30 -0
- package/src/lib/autosave-debug-gate.js +50 -0
- package/src/lib/autosave-scheduler.js +141 -0
- package/src/lib/boot-imports.js +8 -0
- package/src/lib/derived.js +3 -1
- package/src/lib/dirty-gate.js +15 -0
- package/src/lib/doc-storage.js +33 -0
- package/src/lib/effect.js +9 -4
- package/src/lib/mutation.js +4 -2
- package/src/lib/offline-runtime.js +226 -0
- package/src/lib/preload.js +48 -0
- package/src/lib/root-attrs.js +1 -1
- package/src/lib/trash.js +18 -0
- package/src/lib/undo/capture-move.js +152 -0
- package/src/live/local-channel.js +61 -0
- package/src/live/local-lane.js +60 -9
- package/src/live/server-lane.js +6 -3
- package/src/loader-logic.js +76 -15
- package/src/loader.js +218 -30
- package/src/page/adopt-boot.js +76 -22
- package/src/page/adopt.js +25 -4
- package/src/page/awareness.js +230 -0
- package/src/page/blocks.js +79 -5
- package/src/page/blocksel.js +20 -4
- package/src/page/board-face.js +13 -0
- package/src/page/board-panel.js +5 -17
- package/src/page/board-style.js +2 -13
- package/src/page/board-view.js +1 -11
- package/src/page/board-views.js +17 -0
- package/src/page/board.js +2 -2
- package/src/page/boot-pack.js +26 -0
- package/src/page/boot.js +237 -0
- package/src/page/checklist.js +1 -1
- package/src/page/chrome.js +411 -61
- package/src/page/csv-import.js +17 -2
- package/src/page/document-title-dom.js +40 -0
- package/src/page/document-title-runtime.js +76 -0
- package/src/page/document-title.js +9 -0
- package/src/page/drag.js +48 -13
- package/src/page/drop.js +2 -1
- package/src/page/editor.js +330 -86
- package/src/page/feature-loader.js +72 -0
- package/src/page/features/board.js +12 -0
- package/src/page/features/code.js +13 -0
- package/src/page/features/media.js +290 -0
- package/src/page/features/registry.js +23 -0
- package/src/page/features/table.js +383 -0
- package/src/page/{tool-view.js → features/tool.js} +33 -2
- package/src/page/format-codec.js +215 -0
- package/src/page/format-gate.js +179 -0
- package/src/page/format-level.js +157 -0
- package/src/page/highlight-slot.js +23 -0
- package/src/page/highlight.js +30 -16
- package/src/page/history-intent.js +78 -0
- package/src/page/inline.js +18 -25
- package/src/page/items.js +52 -11
- package/src/page/link.js +1 -0
- package/src/page/markdown.js +86 -1
- package/src/page/media-views.js +3 -0
- package/src/page/media.js +33 -245
- package/src/page/outline-cache.js +11 -0
- package/src/page/placeholder.js +27 -7
- package/src/page/projection.js +196 -50
- package/src/page/reader-patch.js +306 -0
- package/src/page/recover.js +1 -1
- package/src/page/rich/page-tool.js +1 -4
- package/src/page/runtime-owner.js +27 -0
- package/src/page/save-projection.js +7 -4
- package/src/page/schema.js +14 -0
- package/src/page/table-rect-key.js +3 -0
- package/src/page/table-select.js +4 -3
- package/src/page/table-style.js +1 -1
- package/src/page/table-views.js +3 -0
- package/src/page/table.js +8 -293
- package/src/page/themes/light-1.css +139 -2
- package/src/page/themes/light-1.source.css +269 -0
- package/src/page/tool-capability.js +3 -0
- package/src/page/tool-views.js +3 -0
- package/src/page/view.js +390 -107
- package/src/page/ybind/awareness.js +172 -0
- package/src/page/ybind/binding.js +9 -1
- package/src/page/ybind/bridge.js +50 -47
- package/src/page/ybind/decode.js +52 -19
- package/src/page/ybind/doc.js +27 -2
- package/src/page/ybind/group.js +12 -3
- package/src/page/ybind/index.js +2 -0
- package/src/page/ybind/lineage.js +71 -0
- package/src/page/ybind/marks.js +25 -1
- package/src/page/ybind/migrate.js +48 -13
- package/src/page/ybind/moves.js +32 -21
- package/src/page/ybind/positions.js +50 -0
- package/src/page/ybind/provider.js +84 -22
- package/src/page/ybind/recovery-proof.js +191 -0
- package/src/page/ybind/recovery-replay.js +130 -0
- package/src/page/ybind/tokens.js +15 -6
- package/src/page/ybind/walk.js +10 -3
- package/src/page/ybind/writer.js +48 -4
- package/src/plugins/ai-edit.js +167 -32
- package/src/plugins/bind-text.js +218 -0
- package/src/plugins/cms.js +93 -0
- package/src/plugins/indicator.js +57 -5
- package/src/plugins/live.js +282 -34
- package/src/plugins/menu/claygoo.svg.js +1 -0
- package/src/plugins/menu/seo-head.js +323 -0
- package/src/plugins/menu/seo.js +849 -0
- package/src/plugins/menu.js +425 -0
- package/src/plugins/page-view.js +131 -0
- package/src/plugins/page.js +1022 -275
- package/src/plugins/people.js +158 -31
- package/src/plugins/richclay.js +119 -9
- package/src/plugins/sortable.js +370 -60
- package/src/plugins/source.js +189 -366
- package/src/plugins/undo.js +289 -19
- package/src/plugins/unsent.js +132 -0
- package/src/plugins/upgrade-run.js +1 -0
- package/src/plugins/upgrade.js +210 -0
- package/src/plugins/upload-task.js +571 -0
- package/src/plugins/upload-ui.js +265 -0
- package/src/plugins/upload.js +34 -153
- package/src/plugins/visitor-drafts/inert.js +32 -0
- package/src/plugins/visitor-drafts/owner-chip.js +3 -0
- package/src/plugins/visitor-drafts/prompt.js +2 -2
- package/src/plugins/visitor-drafts-owner.js +85 -0
- package/src/plugins/visitor-drafts.js +31 -42
- package/src/plugins/wire.js +82 -1
- package/src/reader-core.js +26 -0
- package/src/recovery/conflicts.js +287 -0
- package/src/recovery/identity.js +154 -0
- package/src/recovery/store.js +792 -0
- package/src/recovery/undo.js +257 -0
- package/src/server/checkpoint/index.js +68 -4
- package/src/server/offline-runtime/index.js +214 -0
- package/src/server/rooms/index.js +2 -1
- package/src/server/rooms/live-data-rooms.js +2 -2
- package/src/server/rooms/page-admission.js +33 -0
- package/src/server/rooms/page-lineage.js +56 -0
- package/src/server/rooms/page-rebase.js +11 -5
- package/src/server/rooms/page-state.js +2 -0
- package/src/server/rooms/page-write-admission.js +9 -3
- package/src/source/session.js +705 -0
- package/src/sync/conflict-revert.js +5 -4
- package/src/sync/conflicts-edit.js +119 -0
- package/src/sync/conflicts-facade.js +49 -0
- package/src/sync/conflicts.js +99 -1
- package/src/sync/editing-marker.js +37 -6
- package/src/sync/editing-sender.js +8 -1
- package/src/sync/live-sync-edit.js +63 -0
- package/src/sync/live-sync.js +364 -119
- package/src/sync/local-presence.js +187 -0
- package/src/sync/merge-declarations.js +10 -0
- package/src/sync/merge-engine.js +12 -0
- package/src/sync/presence-store.js +412 -0
- package/src/sync/presence-target.js +243 -0
- package/src/sync/presence.js +72 -41
- package/src/sync/saved-handoff.js +200 -0
- package/src/sync/saved-stream.js +535 -0
- package/src/sync/version-guard.js +92 -0
- package/src/sync/wire-profile.js +81 -0
- package/src/ui/bevel-controls.js +2 -8
- package/src/ui/page-scheme.js +7 -0
- package/src/ui/skins/cms.js +1 -1
- package/src/ui/theme-config.js +150 -0
- package/src/ui/theme.js +6 -138
- package/src/ui/toast-lazy.js +11 -0
- package/src/vendor/LICENSES-optional.md +253 -0
- package/src/vendor/highlight.vendor.js +3 -0
- package/src/vendor/hljs-bash.vendor.js +2 -0
- package/src/vendor/hljs-css.vendor.js +2 -0
- package/src/vendor/hljs-javascript.vendor.js +2 -0
- package/src/vendor/hljs-json.vendor.js +2 -0
- package/src/vendor/hljs-python.vendor.js +2 -0
- package/src/vendor/hljs-xml.vendor.js +2 -0
- package/src/vendor/hyper-html-api-data.vendor.js +1 -1
- package/src/vendor/hyper-html-api-upgrade.vendor.js +5 -0
- package/src/vendor/hyper-morph-identity.vendor.js +2 -0
- package/src/vendor/hyper-morph-json.vendor.js +3 -0
- package/src/vendor/hyper-morph.vendor.js +6 -6
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +169 -84
- package/src/vendor/marked.vendor.js +68 -0
- package/src/vendor/overtype.vendor.js +994 -0
- package/src/vendor/richclay.vendor.js +17 -17
- package/src/vendor/sap-core.vendor.js +5 -5
- package/src/vendor/sap-ownership.vendor.js +11 -7
package/src/plugins/source.js
CHANGED
|
@@ -1,417 +1,240 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* source.js —
|
|
2
|
+
* source.js — the facade over the source map.
|
|
3
3
|
*
|
|
4
4
|
* On by default in edit mode; `exclude=source` turns it off. The mechanism lives in
|
|
5
|
-
* core/source-map.js
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Any of these leaves the save pipeline untouched, and each says so once in the
|
|
35
|
-
* console rather than repeatedly at save time:
|
|
36
|
-
*
|
|
37
|
-
* the source fetch fails, redirects, or is not text/html
|
|
38
|
-
* the fetched bytes disagree with the live document outside <html>
|
|
39
|
-
* the render throws
|
|
40
|
-
* the render does not verify
|
|
5
|
+
* core/source-map.js and the session around it in source/session.js, and neither is
|
|
6
|
+
* imported here: this file is the synchronous API a page reads (`clay.source`), the mode
|
|
7
|
+
* lease, and the one demand that loads the rest.
|
|
8
|
+
*
|
|
9
|
+
* ON DEMAND
|
|
10
|
+
*
|
|
11
|
+
* The parser is 48 KB of parse5 plus the source map, and a page pays for it only when it
|
|
12
|
+
* can change something. `prepare` installs a light lease and fetches nothing, `enable`
|
|
13
|
+
* admits demand, and demand is exactly two things: a save that needs a byte-preserving
|
|
14
|
+
* render, which asks `ensureForSave` from the save pipeline after its cheap gate, and
|
|
15
|
+
* code in edit mode reading `clay.source.ready`. A clean autosave, a reader and a page
|
|
16
|
+
* that never saves never load it, and an idle tail that fires after edit ready loads
|
|
17
|
+
* nothing either.
|
|
18
|
+
*
|
|
19
|
+
* Before the session installs, `text()`, `locate()` and `stats()` answer the unavailable
|
|
20
|
+
* values they always did, with `installed: false`. What the facade keeps in the meantime
|
|
21
|
+
* is the newest bytes this page knows the file holds — the reader's receipt for the
|
|
22
|
+
* session, an accepted save, a disk frame — so the install starts from the file rather
|
|
23
|
+
* than asking the host for a copy it has just written, and a newer receipt supersedes an
|
|
24
|
+
* older one.
|
|
25
|
+
*
|
|
26
|
+
* LIFECYCLE
|
|
27
|
+
*
|
|
28
|
+
* `prepare` installs the light lease, `enable` admits demand, `quiesce` keeps the
|
|
29
|
+
* renderer for the exit's barrier save, and `dispose` takes it back and invalidates any
|
|
30
|
+
* load still in flight: an answer that arrives after the session ended installs nothing.
|
|
31
|
+
* A load that fails is the same logged, non-fatal outcome as before — saves go out exactly
|
|
32
|
+
* as they would without this plugin — and `clay.source.ready` asks again.
|
|
41
33
|
*/
|
|
42
34
|
|
|
43
|
-
import {
|
|
44
|
-
model,
|
|
45
|
-
checkSource,
|
|
46
|
-
locate,
|
|
47
|
-
nodeAt,
|
|
48
|
-
pair,
|
|
49
|
-
render,
|
|
50
|
-
verify
|
|
51
|
-
} from '../core/source-map.js';
|
|
52
|
-
import {
|
|
53
|
-
captureSaveClone,
|
|
54
|
-
originalSnapshotNode,
|
|
55
|
-
setSaveRenderer
|
|
56
|
-
} from '../core/snapshot.js';
|
|
57
35
|
import { onSaveAccepted } from '../core/save-core.js';
|
|
36
|
+
import { startsAtImport } from '../core/is-edit-mode.js';
|
|
58
37
|
|
|
59
|
-
|
|
38
|
+
// Today's answer before the session installs: the same fields `stats()` returns, with
|
|
39
|
+
// `installed: false` and nothing counted.
|
|
40
|
+
const UNAVAILABLE = {
|
|
60
41
|
installed: false,
|
|
61
42
|
refused: null,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
43
|
+
sourceBytes: null,
|
|
44
|
+
paired: 0,
|
|
45
|
+
unresolved: 0,
|
|
46
|
+
unmatchedLive: 0,
|
|
47
|
+
unmatchedSource: 0,
|
|
48
|
+
attrDiffs: 0,
|
|
65
49
|
saves: 0,
|
|
66
50
|
reprints: 0,
|
|
67
51
|
lastReprint: null,
|
|
68
52
|
partialReprints: 0,
|
|
69
53
|
lastPartialReprint: null,
|
|
70
|
-
// What the last render produced, so the save the host accepts can be counted and
|
|
71
|
-
// announced as what it was. Cleared once reported.
|
|
72
|
-
lastOutcome: null,
|
|
73
54
|
lastRenderMs: null,
|
|
74
55
|
lastBytes: null,
|
|
75
|
-
timing: { fetch: null, model: null, pair: null, refresh: null },
|
|
76
56
|
refreshes: 0,
|
|
77
|
-
|
|
78
|
-
pendingBytes: null,
|
|
57
|
+
timing: { fetch: null, model: null, pair: null, refresh: null },
|
|
79
58
|
};
|
|
80
59
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
* `contenteditable="true"` against the file's `inert-contenteditable="true"` and lose
|
|
109
|
-
* the two strongest signature tiers on every activated element and every ancestor of
|
|
110
|
-
* one. The map is keyed through snapshot provenance to the LIVE node, because the
|
|
111
|
-
* clone is rebuilt on every save and its nodes are new objects each time.
|
|
112
|
-
*
|
|
113
|
-
* This capture is an INSPECTION, not a save, and it happens at boot and again on every
|
|
114
|
-
* incoming live-sync frame. So it takes neither of the two things a save capture does
|
|
115
|
-
* on its way past: it does not close the undo batch, which at boot and mid-typing
|
|
116
|
-
* would split the user's undo history at a point they did not make, and it does not
|
|
117
|
-
* run the page's own [onbeforesave] and [onbeforesnapshot] handlers, which are author
|
|
118
|
-
* JavaScript that can do anything and are meant to run once per save. The cost is that
|
|
119
|
-
* a page using those handlers pairs against a tree slightly behind the one it will
|
|
120
|
-
* save, which costs some formatting fidelity on the nodes a handler touches and
|
|
121
|
-
* nothing else, because the render is verified either way.
|
|
122
|
-
*/
|
|
123
|
-
function pairAgainstPage(m) {
|
|
124
|
-
const clone = captureSaveClone({ flushUndo: false, authored: false });
|
|
125
|
-
return pair(clone, m, originalSnapshotNode);
|
|
60
|
+
// The loaded session. Null until something demands it.
|
|
61
|
+
let session = null;
|
|
62
|
+
// The mode entry that prepared this session, as the install needs it: what the reader
|
|
63
|
+
// said this tab represents.
|
|
64
|
+
let context = null;
|
|
65
|
+
// The newest bytes this page knows the file holds, and the version that came with them.
|
|
66
|
+
let receipt = null;
|
|
67
|
+
// Whether demand is admitted: an edit boot's own session, or a lease that was enabled.
|
|
68
|
+
let admitted = startsAtImport();
|
|
69
|
+
// How many sessions have ended. A load that resolves for an older one installs nothing,
|
|
70
|
+
// so a late answer cannot put a renderer or a map back into a view.
|
|
71
|
+
let epoch = 0;
|
|
72
|
+
// The install this session asked for, so two saves in the same breath share it.
|
|
73
|
+
let installing = null;
|
|
74
|
+
// Whether this session's one install attempt has already answered. A save waits for an
|
|
75
|
+
// attempt that is still running and never for one that is done: a refusal and a failed
|
|
76
|
+
// load are both answers, and the next save goes out as it would without the plugin.
|
|
77
|
+
// `clay.source.ready` is the way to ask again, and it retries.
|
|
78
|
+
let answered = false;
|
|
79
|
+
|
|
80
|
+
let loading = null;
|
|
81
|
+
function loadSession() {
|
|
82
|
+
loading ??= import("../source/session.js").catch((error) => {
|
|
83
|
+
loading = null;
|
|
84
|
+
throw error;
|
|
85
|
+
});
|
|
86
|
+
return loading;
|
|
126
87
|
}
|
|
127
88
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const m = model(src);
|
|
133
|
-
const refused = checkSource(m, document);
|
|
134
|
-
if (refused) throw new Error(`source refused: ${refused}`);
|
|
135
|
-
const t2 = now();
|
|
136
|
-
const { map, stats } = pairAgainstPage(m);
|
|
137
|
-
const t3 = now();
|
|
138
|
-
|
|
139
|
-
state.model = m;
|
|
140
|
-
state.map = map;
|
|
141
|
-
state.stats = stats;
|
|
142
|
-
state.timing = { fetch: t1 - t0, model: t2 - t1, pair: t3 - t2, refresh: null };
|
|
143
|
-
state.installed = true;
|
|
144
|
-
setSaveRenderer(renderSave);
|
|
145
|
-
return summary();
|
|
146
|
-
}).catch((err) => {
|
|
147
|
-
// Not an error the page can do anything about, and not a failure of the save:
|
|
148
|
-
// saves go out exactly as they would without this plugin. Said once.
|
|
149
|
-
state.refused = String(err && err.message ? err.message : err);
|
|
150
|
-
console.info(`clayjs: source map off (${state.refused}); saves use the full serialization`);
|
|
151
|
-
return null;
|
|
152
|
-
});
|
|
89
|
+
/** Keep these bytes as what this page believes the file holds. A newer one wins. */
|
|
90
|
+
function retain(bytes, etag, seq) {
|
|
91
|
+
if (typeof bytes !== 'string') return;
|
|
92
|
+
receipt = Object.freeze({ bytes, etag: etag ?? null, seq: seq ?? null });
|
|
153
93
|
}
|
|
154
94
|
|
|
155
95
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* Exported so a test can drive the whole path, including the fallback, without a
|
|
159
|
-
* server: `renderSave(clone, today, { breakText: true })` makes every text node
|
|
160
|
-
* reprint, which changes the bytes while leaving the tree identical, and the verifier
|
|
161
|
-
* must still pass it — a tree comparison is the right answer there, and reading that
|
|
162
|
-
* pass as "the verifier is blind" is a mistake this project has already made once.
|
|
163
|
-
* `{ corrupt: 'drop-first-text' }` is the one that must be REJECTED: it changes the
|
|
164
|
-
* tree, which is exactly what the verifier is for.
|
|
96
|
+
* Start the one install this session needs, or hand back the one already running.
|
|
165
97
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
* author's bytes. Only when the widening reaches <html>, or the failure has no node to
|
|
170
|
-
* point at (the prologue, a parse-error count), is today's full serialization sent.
|
|
171
|
-
*
|
|
172
|
-
* Nothing is counted or announced here. A render is not a save: an autosave that
|
|
173
|
-
* found nothing changed, a refused save and a failed request all rendered. The
|
|
174
|
-
* outcome is recorded, and `adopt` reports it when the host accepts these exact bytes.
|
|
98
|
+
* The session module is imported here and nowhere else, so a page that never demands the
|
|
99
|
+
* parser never evaluates source-map.js. A failed import clears the memo: the fallback is
|
|
100
|
+
* today's save, and the next demand asks again.
|
|
175
101
|
*/
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
for (let round = 0; round <= MAX_PRINT_ROUNDS; round++) {
|
|
186
|
-
let out;
|
|
187
|
-
try {
|
|
188
|
-
out = render(clone, state.map, state.model, originalSnapshotNode, { ...opts, print });
|
|
189
|
-
} catch (err) {
|
|
190
|
-
return fallback('render threw: ' + (err && err.message ? err.message : err), today);
|
|
102
|
+
function installOnce() {
|
|
103
|
+
if (installing) return installing;
|
|
104
|
+
const mine = epoch;
|
|
105
|
+
const entry = context;
|
|
106
|
+
installing = loadSession().then((mod) => {
|
|
107
|
+
session = mod;
|
|
108
|
+
if (mine !== epoch) {
|
|
109
|
+
installing = null;
|
|
110
|
+
return null;
|
|
191
111
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (!v.at || !widen(clone, nodeAt(clone, v.at), print)) return fallback(firstDiff, today);
|
|
203
|
-
}
|
|
204
|
-
return fallback(firstDiff, today);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const MAX_PRINT_ROUNDS = 8;
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Add the next element to print: the one holding `node`, or, when that is already
|
|
211
|
-
* inside a printed element, that element's parent. false when the only element left is
|
|
212
|
-
* the root, which is the full serialization by another name.
|
|
213
|
-
*/
|
|
214
|
-
function widen(clone, node, print) {
|
|
215
|
-
let el = node && node.nodeType === 1 ? node : node && node.parentNode;
|
|
216
|
-
for (let a = el; a && a !== clone; a = a.parentNode) {
|
|
217
|
-
if (print.has(a)) { print.delete(a); el = a.parentNode; break; }
|
|
218
|
-
}
|
|
219
|
-
if (!el || el.nodeType !== 1 || el === clone) return false;
|
|
220
|
-
print.add(el);
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** Send today's full serialization, and remember that this render was a fallback. */
|
|
225
|
-
function fallback(reason, today) {
|
|
226
|
-
state.lastOutcome = { text: today, scope: 'full', reason };
|
|
227
|
-
return today;
|
|
112
|
+
return mod.start({ context: entry, latest: () => receipt });
|
|
113
|
+
}).catch((error) => {
|
|
114
|
+
installing = null;
|
|
115
|
+
console.info('clayjs: source map off (' + (error && error.message ? error.message : error) + '); saves use the full serialization');
|
|
116
|
+
return null;
|
|
117
|
+
}).then((value) => {
|
|
118
|
+
if (mine === epoch) answered = true;
|
|
119
|
+
return value;
|
|
120
|
+
});
|
|
121
|
+
return installing;
|
|
228
122
|
}
|
|
229
123
|
|
|
230
124
|
/**
|
|
231
|
-
* The
|
|
125
|
+
* The save pipeline's question: may this save be rendered through the source map?
|
|
232
126
|
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* exists to stop.
|
|
239
|
-
*/
|
|
240
|
-
function reprint(reason) {
|
|
241
|
-
state.reprints++;
|
|
242
|
-
state.lastReprint = reason;
|
|
243
|
-
console.warn('clayjs: source map did not verify, saving the full serialization instead:', reason);
|
|
244
|
-
document.dispatchEvent(new CustomEvent('clay:save-reprinted', { detail: { reason, scope: 'full' } }));
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Part of the document was printed rather than copied. Counted apart from a full
|
|
249
|
-
* reprint because they are different news: this one kept the author's bytes everywhere
|
|
250
|
-
* else, and its rate says how often the renderer and the page disagree about one
|
|
251
|
-
* element, which is where the next renderer fix is.
|
|
127
|
+
* Called — through `clay.source.ensureForSave`, so a page that excluded this plugin asks
|
|
128
|
+
* nothing — after the cheap gate that says a save may happen and before the one capture.
|
|
129
|
+
* null means there is nothing to wait for: no session for this page, demand not admitted,
|
|
130
|
+
* or this session's attempt has already answered. The save then goes out exactly as it
|
|
131
|
+
* would without this plugin.
|
|
252
132
|
*/
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
console.info(`clayjs: source map printed ${printed} element(s) in full to match the page:`, reason);
|
|
257
|
-
document.dispatchEvent(new CustomEvent('clay:save-reprinted', { detail: { reason, scope: 'partial', printed } }));
|
|
133
|
+
function ensureForSave() {
|
|
134
|
+
if (!admitted || answered) return null;
|
|
135
|
+
return installOnce();
|
|
258
136
|
}
|
|
259
137
|
|
|
260
|
-
/**
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
* It is also where a save is counted, because it is the one place that knows a save
|
|
269
|
-
* reached the file.
|
|
270
|
-
*/
|
|
271
|
-
function adopt(bytes) {
|
|
272
|
-
if (!state.installed) return;
|
|
273
|
-
report(bytes);
|
|
274
|
-
state.pendingBytes = bytes;
|
|
275
|
-
scheduleRefresh();
|
|
138
|
+
/** Take an install back: the renderer, a queued refresh, and any load still in flight. */
|
|
139
|
+
function end() {
|
|
140
|
+
epoch++;
|
|
141
|
+
admitted = false;
|
|
142
|
+
answered = false;
|
|
143
|
+
installing = null;
|
|
144
|
+
context = null;
|
|
145
|
+
if (session) session.release();
|
|
276
146
|
}
|
|
277
147
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const outcome = state.lastOutcome;
|
|
285
|
-
if (!outcome || outcome.text !== bytes) return;
|
|
286
|
-
state.lastOutcome = null;
|
|
287
|
-
state.saves++;
|
|
288
|
-
if (outcome.scope === 'full') reprint(outcome.reason);
|
|
289
|
-
else if (outcome.scope === 'partial') partialReprint(outcome.reason, outcome.printed);
|
|
290
|
-
}
|
|
148
|
+
// An accepted save before the install is the file: the retained receipt becomes these
|
|
149
|
+
// bytes, and the session's superseded answer is what makes a newer one win.
|
|
150
|
+
onSaveAccepted((bytes, accepted) => {
|
|
151
|
+
retain(bytes, receipt ? receipt.etag : null, receipt ? receipt.seq : null);
|
|
152
|
+
if (session && !session.installed()) session.adoptAccepted(bytes, accepted);
|
|
153
|
+
});
|
|
291
154
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
* now on disk, written by somebody else: those become the model, the same as bytes this
|
|
296
|
-
* tab saved, or the next save would copy the old formatting back over theirs. A peer
|
|
297
|
-
* frame changed nothing on disk, so the model stays. The re-pair runs in idle time, or
|
|
298
|
-
* earlier if a save, `text()` or `locate()` needs it first.
|
|
299
|
-
*/
|
|
300
|
-
function queueRepair(event) {
|
|
301
|
-
if (!state.installed) return;
|
|
155
|
+
// A disk frame is somebody else writing the file. Before the install it replaces the
|
|
156
|
+
// receipt the same way; after it, the session's own listener re-models from it.
|
|
157
|
+
document.addEventListener('clay:sync-applied', (event) => {
|
|
302
158
|
const detail = event && event.detail;
|
|
303
|
-
if (detail
|
|
304
|
-
|
|
305
|
-
|
|
159
|
+
if (!detail || detail.source !== 'disk' || typeof detail.html !== 'string') return;
|
|
160
|
+
retain(detail.html, detail.etag, detail.seq);
|
|
161
|
+
if (session && !session.installed()) session.adoptAccepted(detail.html, null);
|
|
162
|
+
});
|
|
306
163
|
|
|
307
|
-
/**
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
* Coalescing is what makes this safe as well as cheap. Saves are serialized, so the
|
|
311
|
-
* most recent accepted bytes are the file; a refresh that skipped an intermediate
|
|
312
|
-
* save would still land on the right answer, and one that ran them out of order could
|
|
313
|
-
* not.
|
|
314
|
-
*/
|
|
315
|
-
function scheduleRefresh() {
|
|
316
|
-
if (state.refreshQueued) return;
|
|
317
|
-
state.refreshQueued = true;
|
|
318
|
-
if (typeof requestIdleCallback === 'function') requestIdleCallback(refreshNow, { timeout: 2000 });
|
|
319
|
-
else setTimeout(refreshNow, 0);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Run a queued refresh now. A no-op when none is queued, which is also what makes the
|
|
324
|
-
* idle callback of a refresh that `text()` or `locate()` already ran harmless.
|
|
325
|
-
*/
|
|
326
|
-
function refreshNow() {
|
|
327
|
-
if (!state.refreshQueued) return;
|
|
328
|
-
state.refreshQueued = false;
|
|
329
|
-
if (!state.installed) return;
|
|
330
|
-
const bytes = state.pendingBytes;
|
|
331
|
-
state.pendingBytes = null;
|
|
332
|
-
const t = now();
|
|
333
|
-
// The two halves fail independently, so they are tried independently. A refused
|
|
334
|
-
// re-model used to skip the re-pair with it, and the re-pair is the half that
|
|
335
|
-
// cannot be skipped: a morph replaced live nodes, so the old map is keyed by
|
|
336
|
-
// objects no longer in the page, and every save after that reprints the whole
|
|
337
|
-
// document until something else queues a refresh. The previous model still
|
|
338
|
-
// describes real bytes, so re-pairing against it is the right answer.
|
|
339
|
-
let m = state.model;
|
|
340
|
-
if (bytes !== null) {
|
|
341
|
-
try {
|
|
342
|
-
const next = model(bytes);
|
|
343
|
-
const refused = checkSource(next, document);
|
|
344
|
-
if (refused) throw new Error(refused);
|
|
345
|
-
m = next;
|
|
346
|
-
} catch (err) {
|
|
347
|
-
console.warn('clayjs: source map kept the previous model, the accepted bytes did not model:', err);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
try {
|
|
351
|
-
const { map, stats } = pairAgainstPage(m);
|
|
352
|
-
state.model = m;
|
|
353
|
-
state.map = map;
|
|
354
|
-
state.stats = stats;
|
|
355
|
-
state.refreshes++;
|
|
356
|
-
state.timing.refresh = now() - t;
|
|
357
|
-
} catch (err) {
|
|
358
|
-
// The map is now stale rather than wrong: it still describes the pairing as of
|
|
359
|
-
// the last successful refresh. Renders off a stale map verify or fall back like
|
|
360
|
-
// any other, so this costs formatting fidelity and nothing else.
|
|
361
|
-
console.warn('clayjs: source map could not re-pair, continuing on the previous map:', err);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function summary() {
|
|
366
|
-
const s = state.stats;
|
|
367
|
-
return {
|
|
368
|
-
installed: state.installed,
|
|
369
|
-
refused: state.refused,
|
|
370
|
-
sourceBytes: state.model ? state.model.src.length : null,
|
|
371
|
-
paired: s ? s.paired : 0,
|
|
372
|
-
unresolved: s ? s.unresolved : 0,
|
|
373
|
-
unmatchedLive: s ? s.unmatchedLive.length : 0,
|
|
374
|
-
unmatchedSource: s ? s.unmatchedSource.length : 0,
|
|
375
|
-
attrDiffs: s ? s.attrDiffs.length : 0,
|
|
376
|
-
saves: state.saves,
|
|
377
|
-
reprints: state.reprints,
|
|
378
|
-
lastReprint: state.lastReprint,
|
|
379
|
-
partialReprints: state.partialReprints,
|
|
380
|
-
lastPartialReprint: state.lastPartialReprint,
|
|
381
|
-
lastRenderMs: state.lastRenderMs,
|
|
382
|
-
lastBytes: state.lastBytes,
|
|
383
|
-
refreshes: state.refreshes,
|
|
384
|
-
timing: state.timing,
|
|
385
|
-
};
|
|
164
|
+
/** Render one save through the loaded session, or hand back today's bytes. */
|
|
165
|
+
export function renderSave(clone, today, opts = {}) {
|
|
166
|
+
return session ? session.renderSave(clone, today, opts) : today;
|
|
386
167
|
}
|
|
387
168
|
|
|
388
169
|
export const source = {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
170
|
+
/**
|
|
171
|
+
* The install, in edit mode: reading this is the second thing that demands the parser.
|
|
172
|
+
* Null where there is nothing to install, which is what a view and a page without the
|
|
173
|
+
* plugin have always answered.
|
|
174
|
+
*/
|
|
175
|
+
get ready() {
|
|
176
|
+
if (!admitted) return null;
|
|
177
|
+
return installOnce();
|
|
178
|
+
},
|
|
179
|
+
stats: () => (session ? session.stats() : { ...UNAVAILABLE }),
|
|
180
|
+
/** The bytes this module believes are on disk right now. */
|
|
181
|
+
text: () => (session ? session.text() : null),
|
|
393
182
|
/**
|
|
394
183
|
* Where a live element is in those bytes: `{ from, to, line, column }`, or null.
|
|
395
184
|
*
|
|
396
|
-
* Offsets and `column` are UTF-16 code units into `text()`, not bytes.
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
* one customer's file.
|
|
400
|
-
*
|
|
401
|
-
* null whenever this module cannot answer, which includes the whole document when the plugin
|
|
402
|
-
* never installed or refused it. `stats().installed` and `stats().refused` say which, and an
|
|
403
|
-
* agent that cannot tell "this element is new" from "this document has no map" will eventually
|
|
404
|
-
* write into a file it was never modelling.
|
|
185
|
+
* Offsets and `column` are UTF-16 code units into `text()`, not bytes. null whenever this
|
|
186
|
+
* module cannot answer, which includes the whole document before the session installed.
|
|
187
|
+
* `stats().installed` and `stats().refused` say which.
|
|
405
188
|
*/
|
|
406
|
-
locate: (node) =>
|
|
189
|
+
locate: (node) => (session ? session.locateLive(node) : null),
|
|
407
190
|
/** What did not pair, for working out why a document reprints more than it should. */
|
|
408
|
-
unpaired: () =>
|
|
409
|
-
|
|
410
|
-
|
|
191
|
+
unpaired: () => (session ? session.unpaired() : null),
|
|
192
|
+
/** The save pipeline's own ask: a promise to wait for before its capture, or null. */
|
|
193
|
+
ensureForSave,
|
|
411
194
|
};
|
|
412
195
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
196
|
+
/**
|
|
197
|
+
* The source map as a mode lease.
|
|
198
|
+
*
|
|
199
|
+
* `prepare` records the file this session represents and installs the light lease; it
|
|
200
|
+
* fetches nothing and pairs nothing. `enable` admits demand, which is the only thing that
|
|
201
|
+
* belongs to the commit. `quiesce` keeps the renderer for the exit's barrier save,
|
|
202
|
+
* `resume` admits demand again after a refused exit, and `dispose` takes the session back
|
|
203
|
+
* and invalidates whatever was still loading.
|
|
204
|
+
*
|
|
205
|
+
* `initial: true` is this tab's own edit boot, whose session is this one: the lease adopts
|
|
206
|
+
* it, and the boot's demand installed — or did not — exactly as it would have.
|
|
207
|
+
*/
|
|
208
|
+
export function sourceParticipant() {
|
|
209
|
+
return {
|
|
210
|
+
id: 'clay:source',
|
|
211
|
+
// After the save session (10), whose saves this renderer answers, and before the
|
|
212
|
+
// page (400), whose editor takes the article the pairing is taken over.
|
|
213
|
+
order: 30,
|
|
214
|
+
prepare(entry = {}) {
|
|
215
|
+
const represented = entry && entry.represented;
|
|
216
|
+
if (represented) {
|
|
217
|
+
context = { represented };
|
|
218
|
+
retain(represented.bytes, represented.etag, represented.seq);
|
|
219
|
+
}
|
|
220
|
+
if (entry && entry.initial === true) {
|
|
221
|
+
admitted = true;
|
|
222
|
+
return { enable() {}, quiesce() {}, resume() {}, dispose: end };
|
|
223
|
+
}
|
|
224
|
+
const mine = ++epoch;
|
|
225
|
+
admitted = false;
|
|
226
|
+
answered = false;
|
|
227
|
+
installing = null;
|
|
228
|
+
return {
|
|
229
|
+
enable() { if (mine === epoch) admitted = true; },
|
|
230
|
+
quiesce() {},
|
|
231
|
+
resume() { if (mine === epoch) admitted = true; },
|
|
232
|
+
dispose() { if (mine === epoch) end(); },
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export { sourceParticipant as modeParticipant };
|
|
416
239
|
|
|
417
240
|
export default source;
|