@panphora/clayjs 1.15.1 → 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/lib/dirty-gate.js
CHANGED
|
@@ -51,6 +51,9 @@ function scheduleAnnounce() {
|
|
|
51
51
|
const dirty = pageMaybeDirty();
|
|
52
52
|
if (dirty === announcedDirty) return;
|
|
53
53
|
announcedDirty = dirty;
|
|
54
|
+
const root = document.documentElement;
|
|
55
|
+
if (dirty) root.setAttribute('unsaved', 'true');
|
|
56
|
+
else root.removeAttribute('unsaved');
|
|
54
57
|
document.dispatchEvent(new CustomEvent(dirty ? 'clay:dirty' : 'clay:clean'));
|
|
55
58
|
});
|
|
56
59
|
}
|
|
@@ -182,6 +185,18 @@ export function pageMaybeDirty() {
|
|
|
182
185
|
return changes > clearedAt || persistProbeDirty();
|
|
183
186
|
}
|
|
184
187
|
|
|
188
|
+
/**
|
|
189
|
+
* How many changes this page has recorded, ever.
|
|
190
|
+
*
|
|
191
|
+
* Monotonic and never rewound by a save or a clear, so a caller that closed input
|
|
192
|
+
* admission can record it and later prove that nothing new arrived while it was closed,
|
|
193
|
+
* instead of assuming quiet. Observation keeps running while admission is closed: what
|
|
194
|
+
* stops is writing, never the record of work.
|
|
195
|
+
*/
|
|
196
|
+
export function gateRevision() {
|
|
197
|
+
return changes;
|
|
198
|
+
}
|
|
199
|
+
|
|
185
200
|
/**
|
|
186
201
|
* Record the gate state at capture time. The save flow takes a token when it
|
|
187
202
|
* clones the DOM and hands it back on success (or on a verified "no changes"),
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Browser storage for one document. Two documents on one origin (every file on a
|
|
2
|
+
// hyperclay.com subdomain) otherwise share every key an app writes.
|
|
3
|
+
export const docKey = (key) => `clay:doc:${location.pathname}:${key}`;
|
|
4
|
+
|
|
5
|
+
function area(name) {
|
|
6
|
+
const memory = new Map();
|
|
7
|
+
// Keys whose last write or removal could not reach the backend. Memory holds
|
|
8
|
+
// the authoritative value (or the removal) for them until one write lands.
|
|
9
|
+
const overrides = new Set();
|
|
10
|
+
const prefix = () => docKey("");
|
|
11
|
+
const backend = () => { try { return window[name] || null; } catch { return null; } };
|
|
12
|
+
return {
|
|
13
|
+
get(key) {
|
|
14
|
+
const k = prefix() + key;
|
|
15
|
+
if (!overrides.has(k)) {
|
|
16
|
+
try { const raw = backend()?.getItem(k); if (raw != null) return JSON.parse(raw); } catch {}
|
|
17
|
+
}
|
|
18
|
+
return memory.has(k) ? memory.get(k) : undefined;
|
|
19
|
+
},
|
|
20
|
+
set(key, value) {
|
|
21
|
+
const k = prefix() + key;
|
|
22
|
+
memory.set(k, value);
|
|
23
|
+
try { backend().setItem(k, JSON.stringify(value)); overrides.delete(k); return true; } catch { overrides.add(k); return false; }
|
|
24
|
+
},
|
|
25
|
+
remove(key) {
|
|
26
|
+
const k = prefix() + key;
|
|
27
|
+
memory.delete(k);
|
|
28
|
+
try { backend().removeItem(k); overrides.delete(k); } catch { overrides.add(k); }
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const storage = { local: area("localStorage"), session: area("sessionStorage") };
|
package/src/lib/effect.js
CHANGED
|
@@ -110,17 +110,22 @@ export function effect(fn) {
|
|
|
110
110
|
queueMicrotask(run);
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
+
const undoEvents = ["undo", "redo"];
|
|
114
|
+
const bindUndo = () => {
|
|
115
|
+
for (const type of undoEvents) clay.undo?.on?.(type, schedule);
|
|
116
|
+
};
|
|
117
|
+
|
|
113
118
|
const offMutation = Mutation.onAnyChange({ debounce: 0, require: "observed" }, schedule);
|
|
114
119
|
document.addEventListener("clay:sync-applied", schedule);
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
document.addEventListener("clay:modechange", bindUndo);
|
|
121
|
+
bindUndo();
|
|
117
122
|
|
|
118
123
|
const stop = () => {
|
|
119
124
|
stopped = true;
|
|
120
125
|
offMutation();
|
|
121
126
|
document.removeEventListener("clay:sync-applied", schedule);
|
|
122
|
-
|
|
123
|
-
clay.undo?.off?.(
|
|
127
|
+
document.removeEventListener("clay:modechange", bindUndo);
|
|
128
|
+
for (const type of undoEvents) clay.undo?.off?.(type, schedule);
|
|
124
129
|
};
|
|
125
130
|
|
|
126
131
|
schedule();
|
package/src/lib/mutation.js
CHANGED
|
@@ -150,11 +150,13 @@ const localMutation = {
|
|
|
150
150
|
// Records the browser queued before this pause describe changes made outside
|
|
151
151
|
// it. Left in the queue, resume() would drain them as paused-time records and
|
|
152
152
|
// only non-pausable callbacks would see them, so autosave missed a change made
|
|
153
|
-
// just before a pause. Deliver them
|
|
153
|
+
// just before a pause. Deliver them NOW, before the depth rises and before the
|
|
154
|
+
// pause is bridged to undo: a deferred delivery arrived while the raw lane was
|
|
155
|
+
// paused, and undo's paused sink drops what it sees then.
|
|
154
156
|
_deliverBeforePause() {
|
|
155
157
|
if (!this._observer) return;
|
|
156
158
|
const records = this._observer.takeRecords();
|
|
157
|
-
if (records.length)
|
|
159
|
+
if (records.length) this._onRecords(records);
|
|
158
160
|
},
|
|
159
161
|
|
|
160
162
|
/**
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
export const LOCAL_PREFIX = "/_/clayjs/";
|
|
2
|
+
|
|
3
|
+
const CONTROL = /[\u0000-\u001F\u007F]/;
|
|
4
|
+
const ASCII_EDGE_SPACE = /^[\t\n\f\r ]|[\t\n\f\r ]$/;
|
|
5
|
+
const ROLLING = /^https:\/\/clayjs\.com\/v(\d+)\/([\s\S]*)$/;
|
|
6
|
+
const EXACT = /^https:\/\/clayjs\.com\/(\d+)\.(\d+)\.(\d+)\/([\s\S]*)$/;
|
|
7
|
+
const LOCAL = /^\/_\/clayjs\/(\d+)\.(\d+)\.(\d+)\/([\s\S]*)$/;
|
|
8
|
+
const SUSPECT = /%2e|%2f|%5c/i;
|
|
9
|
+
|
|
10
|
+
function majorOf(version) {
|
|
11
|
+
if (typeof version !== "string") return null;
|
|
12
|
+
const match = /^(\d+)\.\d+\.\d+$/.exec(version);
|
|
13
|
+
return match ? match[1] : null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function originOf(documentURL) {
|
|
17
|
+
if (typeof documentURL !== "string" || documentURL === "") return null;
|
|
18
|
+
try {
|
|
19
|
+
return new URL(documentURL).origin;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function splitURL(raw) {
|
|
26
|
+
const hashAt = raw.indexOf("#");
|
|
27
|
+
const hash = hashAt < 0 ? "" : raw.slice(hashAt);
|
|
28
|
+
const withoutHash = hashAt < 0 ? raw : raw.slice(0, hashAt);
|
|
29
|
+
const searchAt = withoutHash.indexOf("?");
|
|
30
|
+
const search = searchAt < 0 ? "" : withoutHash.slice(searchAt);
|
|
31
|
+
const head = searchAt < 0 ? withoutHash : withoutHash.slice(0, searchAt);
|
|
32
|
+
return { head, search, hash };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function decodePath(rawPath) {
|
|
36
|
+
if (rawPath === "") return null;
|
|
37
|
+
if (rawPath.includes("\\")) return null;
|
|
38
|
+
if (SUSPECT.test(rawPath)) return null;
|
|
39
|
+
const segments = rawPath.split("/");
|
|
40
|
+
const decoded = [];
|
|
41
|
+
for (const segment of segments) {
|
|
42
|
+
if (segment === "" || segment === "." || segment === "..") return null;
|
|
43
|
+
let value;
|
|
44
|
+
try {
|
|
45
|
+
value = decodeURIComponent(segment);
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (value.includes("/") || value.includes("\\")) return null;
|
|
50
|
+
if (value === "." || value === "..") return null;
|
|
51
|
+
decoded.push(value);
|
|
52
|
+
}
|
|
53
|
+
return decoded.join("/");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function parseRuntimeURL(raw, { documentURL } = {}) {
|
|
57
|
+
if (typeof raw !== "string") return null;
|
|
58
|
+
if (raw === "") return null;
|
|
59
|
+
if (ASCII_EDGE_SPACE.test(raw) || CONTROL.test(raw)) return null;
|
|
60
|
+
const { head, search, hash } = splitURL(raw);
|
|
61
|
+
|
|
62
|
+
let kind = null;
|
|
63
|
+
let version = null;
|
|
64
|
+
let major = null;
|
|
65
|
+
let rawPath = null;
|
|
66
|
+
|
|
67
|
+
let match = ROLLING.exec(head);
|
|
68
|
+
if (match) {
|
|
69
|
+
kind = "rolling";
|
|
70
|
+
major = match[1];
|
|
71
|
+
rawPath = match[2];
|
|
72
|
+
}
|
|
73
|
+
if (!kind) {
|
|
74
|
+
match = EXACT.exec(head);
|
|
75
|
+
if (match) {
|
|
76
|
+
kind = "exact";
|
|
77
|
+
version = `${match[1]}.${match[2]}.${match[3]}`;
|
|
78
|
+
major = match[1];
|
|
79
|
+
rawPath = match[4];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!kind) {
|
|
83
|
+
match = LOCAL.exec(head);
|
|
84
|
+
if (match) {
|
|
85
|
+
kind = "local";
|
|
86
|
+
version = `${match[1]}.${match[2]}.${match[3]}`;
|
|
87
|
+
major = match[1];
|
|
88
|
+
rawPath = match[4];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!kind) {
|
|
92
|
+
const origin = originOf(documentURL);
|
|
93
|
+
if (origin && head.startsWith(`${origin}/`)) {
|
|
94
|
+
match = LOCAL.exec(head.slice(origin.length));
|
|
95
|
+
if (match) {
|
|
96
|
+
kind = "local";
|
|
97
|
+
version = `${match[1]}.${match[2]}.${match[3]}`;
|
|
98
|
+
major = match[1];
|
|
99
|
+
rawPath = match[4];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!kind) return null;
|
|
104
|
+
|
|
105
|
+
const path = decodePath(rawPath);
|
|
106
|
+
if (path === null) return null;
|
|
107
|
+
|
|
108
|
+
return { kind, major, version, rawPath, path, search, hash };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function filesOf(releases, version) {
|
|
112
|
+
const release = releases && typeof releases === "object" ? releases[version] : null;
|
|
113
|
+
return release && typeof release.files === "object" && release.files ? release.files : null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function resolveRuntimeURL(raw, { releases, selectedVersion, documentURL } = {}) {
|
|
117
|
+
const selected = filesOf(releases, selectedVersion);
|
|
118
|
+
if (selected) {
|
|
119
|
+
const aliases = releases[selectedVersion].aliases;
|
|
120
|
+
if (aliases && typeof aliases === "object" && Object.hasOwn(aliases, raw)) {
|
|
121
|
+
const target = aliases[raw];
|
|
122
|
+
return {
|
|
123
|
+
kind: "alias",
|
|
124
|
+
version: selectedVersion,
|
|
125
|
+
path: target,
|
|
126
|
+
search: "",
|
|
127
|
+
hash: "",
|
|
128
|
+
available: true,
|
|
129
|
+
canonicalURL: raw,
|
|
130
|
+
localURL: `${LOCAL_PREFIX}${selectedVersion}/${target}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const parsed = parseRuntimeURL(raw, { documentURL });
|
|
136
|
+
if (!parsed) return null;
|
|
137
|
+
if (parsed.kind === "local") return null;
|
|
138
|
+
|
|
139
|
+
let version;
|
|
140
|
+
if (parsed.kind === "rolling") {
|
|
141
|
+
if (majorOf(selectedVersion) === null || majorOf(selectedVersion) !== parsed.major) return null;
|
|
142
|
+
version = selectedVersion;
|
|
143
|
+
} else {
|
|
144
|
+
version = parsed.version;
|
|
145
|
+
if (!filesOf(releases, version)) {
|
|
146
|
+
return {
|
|
147
|
+
kind: "exact",
|
|
148
|
+
version,
|
|
149
|
+
path: parsed.path,
|
|
150
|
+
search: parsed.search,
|
|
151
|
+
hash: parsed.hash,
|
|
152
|
+
available: false,
|
|
153
|
+
canonicalURL: raw,
|
|
154
|
+
localURL: null,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const files = filesOf(releases, version);
|
|
160
|
+
if (!files || !Object.hasOwn(files, parsed.path)) return null;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
kind: parsed.kind,
|
|
164
|
+
version,
|
|
165
|
+
path: parsed.path,
|
|
166
|
+
search: parsed.search,
|
|
167
|
+
hash: parsed.hash,
|
|
168
|
+
available: true,
|
|
169
|
+
canonicalURL: raw,
|
|
170
|
+
localURL: `${LOCAL_PREFIX}${version}/${parsed.rawPath}${parsed.search}${parsed.hash}`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function entriesOf(descriptor) {
|
|
175
|
+
if (!descriptor || typeof descriptor !== "object") return [];
|
|
176
|
+
if (!Array.isArray(descriptor.entries)) return [];
|
|
177
|
+
return descriptor.entries.filter((entry) => entry && typeof entry === "object");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function originalMatchesRelease(original, version, releases) {
|
|
181
|
+
if (!releases) return true;
|
|
182
|
+
const files = filesOf(releases, version);
|
|
183
|
+
return Boolean(files && Object.hasOwn(files, original.path));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function canonicalizeRuntimeURL(value, { descriptor, releases, documentURL, aliases } = {}) {
|
|
187
|
+
const local = parseRuntimeURL(value, { documentURL });
|
|
188
|
+
if (!local || local.kind !== "local") return value;
|
|
189
|
+
|
|
190
|
+
const entries = entriesOf(descriptor);
|
|
191
|
+
const localMajor = majorOf(local.version);
|
|
192
|
+
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
if (entry.local !== value || typeof entry.original !== "string") continue;
|
|
195
|
+
const trusted = releases?.[local.version]?.aliases ?? aliases;
|
|
196
|
+
if (trusted && typeof trusted === "object" && !local.search && !local.hash &&
|
|
197
|
+
Object.hasOwn(trusted, entry.original) && trusted[entry.original] === local.path) {
|
|
198
|
+
return entry.original;
|
|
199
|
+
}
|
|
200
|
+
const original = parseRuntimeURL(entry.original);
|
|
201
|
+
if (!original) continue;
|
|
202
|
+
if (original.kind !== "rolling" && original.kind !== "exact") continue;
|
|
203
|
+
if (original.rawPath !== local.rawPath) continue;
|
|
204
|
+
if (original.search !== local.search) continue;
|
|
205
|
+
if (original.hash !== local.hash) continue;
|
|
206
|
+
if (original.kind === "rolling" ? original.major !== localMajor : original.version !== local.version) continue;
|
|
207
|
+
if (!originalMatchesRelease(local, local.version, releases)) continue;
|
|
208
|
+
if (original.kind === "exact") return entry.original;
|
|
209
|
+
return `https://clayjs.com/v${original.major}/${local.rawPath}${local.search}${local.hash}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const entry of entries) {
|
|
213
|
+
if (typeof entry.original !== "string") continue;
|
|
214
|
+
const original = parseRuntimeURL(entry.original);
|
|
215
|
+
if (!original) continue;
|
|
216
|
+
if (original.kind !== "rolling" && original.kind !== "exact") continue;
|
|
217
|
+
if (original.rawPath !== local.rawPath) continue;
|
|
218
|
+
if (original.kind === "rolling" ? original.major !== localMajor : original.version !== local.version) continue;
|
|
219
|
+
if (original.kind === "rolling") {
|
|
220
|
+
return `https://clayjs.com/v${original.major}/${local.rawPath}${local.search}${local.hash}`;
|
|
221
|
+
}
|
|
222
|
+
return `https://clayjs.com/${original.version}/${local.rawPath}${local.search}${local.hash}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return `https://clayjs.com/${local.version}/${local.rawPath}${local.search}${local.hash}`;
|
|
226
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// A reader fetched only the manifest; an editable page also fetched the supplement.
|
|
2
|
+
// Both are hint lists, so a boot holding one still runs and defers the other's hints.
|
|
3
|
+
export function mergeManifests(manifest, supplement) {
|
|
4
|
+
if (!manifest || !supplement) return manifest;
|
|
5
|
+
const both = { ...supplement, ...manifest };
|
|
6
|
+
for (const key of ["entries", "imports", "bootImports"]) both[key] = { ...supplement[key], ...manifest[key] };
|
|
7
|
+
return both;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// The chunked build's files that a boot plan will import, fetched in parallel before
|
|
11
|
+
// boot() imports them one at a time in its load-bearing order. clay="no-save" keeps
|
|
12
|
+
// the links out of every save and out of live sync's merge.
|
|
13
|
+
const PAGE_PLUGIN = "plugins/page.js";
|
|
14
|
+
|
|
15
|
+
export function preloadEntries(manifest, paths, { isEditMode = false, isDraftMode = false, hasSapJs = false } = {}, doc = document) {
|
|
16
|
+
if (!manifest?.entries) return 0;
|
|
17
|
+
const seeds = [...paths];
|
|
18
|
+
for (const path of paths) {
|
|
19
|
+
for (const file of manifest.bootImports?.[path] ?? []) {
|
|
20
|
+
if (hasSapJs && file === "vendor/sap-core.vendor.js") continue;
|
|
21
|
+
seeds.push(file);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (isEditMode && !isDraftMode) seeds.push(...(manifest.editBootImports ?? []));
|
|
25
|
+
// The editor is fetched beside the room join, so a plan that runs the page plugin
|
|
26
|
+
// preloads it and another template's edit boot does not.
|
|
27
|
+
if (isEditMode && !isDraftMode && paths.includes(PAGE_PLUGIN)) seeds.push(...(manifest.editorBootImports ?? []));
|
|
28
|
+
const want = new Set();
|
|
29
|
+
const add = (file) => {
|
|
30
|
+
if (!file || want.has(file)) return;
|
|
31
|
+
want.add(file);
|
|
32
|
+
for (const dep of manifest.imports?.[file] ?? []) add(dep);
|
|
33
|
+
};
|
|
34
|
+
for (const p of seeds) add(manifest.entries[p]);
|
|
35
|
+
const have = new Set([...doc.querySelectorAll('link[rel="modulepreload"]')].map((link) => link.href));
|
|
36
|
+
let added = 0;
|
|
37
|
+
for (const file of want) {
|
|
38
|
+
const href = new URL(file, manifest.base).href;
|
|
39
|
+
if (have.has(href)) continue;
|
|
40
|
+
const link = doc.createElement("link");
|
|
41
|
+
link.rel = "modulepreload";
|
|
42
|
+
link.href = href;
|
|
43
|
+
link.setAttribute("clay", "no-save");
|
|
44
|
+
doc.head.append(link);
|
|
45
|
+
added++;
|
|
46
|
+
}
|
|
47
|
+
return added;
|
|
48
|
+
}
|
package/src/lib/root-attrs.js
CHANGED
|
@@ -94,7 +94,7 @@ export const HOST_TOKEN_ATTRS = [
|
|
|
94
94
|
];
|
|
95
95
|
|
|
96
96
|
// This library's own root state, and this tab's UI truth.
|
|
97
|
-
export const ROOT_LIBRARY_ATTRS = ["savestatus", "editmode", "pageowner", "draftmode"];
|
|
97
|
+
export const ROOT_LIBRARY_ATTRS = ["savestatus", "editmode", "pageowner", "draftmode", "unsaved", "canedit"];
|
|
98
98
|
|
|
99
99
|
// Never copied between tabs: not written onto this root by an incoming morph, not
|
|
100
100
|
// sent out on a sync broadcast.
|
package/src/lib/trash.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Soft delete. A record in the trash keeps its place, id and content and carries
|
|
2
|
+
// data-deleted-at; restoring removes the attribute; purging removes the record.
|
|
3
|
+
export const DELETED_ATTR = "data-deleted-at";
|
|
4
|
+
const run = (label, fn) => (window.clay?.undo?.commit ? window.clay.undo.commit(label, fn) : fn());
|
|
5
|
+
const tell = (el, type) => el.dispatchEvent(new CustomEvent(type, { bubbles: true }));
|
|
6
|
+
|
|
7
|
+
export const trash = Object.assign(
|
|
8
|
+
(el, { at = new Date().toISOString() } = {}) => {
|
|
9
|
+
run("Move to Trash", () => el.setAttribute(DELETED_ATTR, at));
|
|
10
|
+
tell(el, "clay:trashed");
|
|
11
|
+
return { undo: () => trash.restore(el) };
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
is: (el) => !!el.getAttribute(DELETED_ATTR),
|
|
15
|
+
restore(el) { run("Restore", () => el.removeAttribute(DELETED_ATTR)); tell(el, "clay:restored"); },
|
|
16
|
+
purge(el) { run("Delete forever", () => el.remove()); },
|
|
17
|
+
},
|
|
18
|
+
);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// One drag or one `clay.sortable.move` as one semantic undo entry.
|
|
2
|
+
//
|
|
3
|
+
// The engine records primitives, not gestures. A move is the item leaving its source
|
|
4
|
+
// slot and arriving in the destination slot, so the entry is that remove and that add
|
|
5
|
+
// recorded inside `undo.command`; the engine pairs them into its own `move` step,
|
|
6
|
+
// checked against the item's identity and both sequences, so undo puts the card back
|
|
7
|
+
// without replaying the drag and redo moves it again.
|
|
8
|
+
//
|
|
9
|
+
// A gesture also writes while it happens. Sortable marks the item it picks up
|
|
10
|
+
// draggable and leaves style="" behind, and those writes are runtime, not authored
|
|
11
|
+
// content: they are ignored while the gesture is open (isRuntimeAttribute), and the
|
|
12
|
+
// writes the end of the gesture makes are held out of the automatic idle batch
|
|
13
|
+
// (holdMove/releaseMove) so a drag cannot also become a generic `Edit` entry.
|
|
14
|
+
|
|
15
|
+
const RUNTIME_ATTRS = new Set(['draggable', 'style'])
|
|
16
|
+
|
|
17
|
+
// The presses whose gesture may still be writing. Bounded: a press joins on pointerdown
|
|
18
|
+
// and leaves on its end, its cancel, or the session's dispose.
|
|
19
|
+
const open = new Set()
|
|
20
|
+
|
|
21
|
+
const engine = () => window.clay?.undo
|
|
22
|
+
|
|
23
|
+
const sameNodes = (a, b) => a.length === b.length && a.every((node, i) => node === b[i])
|
|
24
|
+
|
|
25
|
+
// The label a person reads in the history. A container names its own moves; a list
|
|
26
|
+
// that does not is simply "Move".
|
|
27
|
+
function moveLabel(result) {
|
|
28
|
+
const named = result.from?.getAttribute?.('sortable-label') || result.to?.getAttribute?.('sortable-label')
|
|
29
|
+
return named || 'Move'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The press: what a drag started with, before the vendor touched anything.
|
|
33
|
+
export function beginMove(item) {
|
|
34
|
+
const from = item?.parentElement
|
|
35
|
+
if (!item || !from) return null
|
|
36
|
+
const start = {
|
|
37
|
+
item,
|
|
38
|
+
from,
|
|
39
|
+
children: [...from.children],
|
|
40
|
+
next: item.nextSibling,
|
|
41
|
+
previous: item.previousSibling,
|
|
42
|
+
nodes: new Set([item, ...item.querySelectorAll('*')]),
|
|
43
|
+
held: false,
|
|
44
|
+
}
|
|
45
|
+
open.add(start)
|
|
46
|
+
return start
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// A runtime attribute of the item a gesture holds: the vendor's own bookkeeping, which
|
|
50
|
+
// a mere click would otherwise write into the file and into history.
|
|
51
|
+
export function isRuntimeAttribute(name, target) {
|
|
52
|
+
if (!RUNTIME_ATTRS.has(name)) return false
|
|
53
|
+
for (const start of open) if (start.nodes.has(target)) return true
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The item a gesture holds: the vendor moves it many times during dragover or
|
|
58
|
+
// touchmove, and none of those moves is an edit; the one semantic move is recorded
|
|
59
|
+
// at the end.
|
|
60
|
+
export function isGestureItem(node) {
|
|
61
|
+
for (const start of open) if (start.item === node) return true
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The engine's own exclusion bracket, opened before the gesture's final writes and
|
|
66
|
+
// closed right after its one entry: those writes reach neither the idle batch nor a
|
|
67
|
+
// command's capture. Reference-counted by the engine, so a release that arrives twice
|
|
68
|
+
// is harmless.
|
|
69
|
+
export function holdMove(start) {
|
|
70
|
+
const undo = engine()
|
|
71
|
+
if (!start || start.held || !undo?.pause) return false
|
|
72
|
+
undo.pause()
|
|
73
|
+
start.held = true
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function releaseMove(start) {
|
|
78
|
+
if (!start) return
|
|
79
|
+
open.delete(start)
|
|
80
|
+
if (!start.held) return
|
|
81
|
+
start.held = false
|
|
82
|
+
engine()?.resume?.()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function releaseAllMoves() {
|
|
86
|
+
for (const start of [...open]) releaseMove(start)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Something outside this gesture changed the source list, or took the item away. The
|
|
90
|
+
// start coordinates describe a list that is no longer there, so nothing is recorded:
|
|
91
|
+
// the card stays where the merged document put it, undo does not claim it, and the
|
|
92
|
+
// page is told why.
|
|
93
|
+
function drifted(start, result) {
|
|
94
|
+
if (!start.item.isConnected || start.item.parentElement !== result.to) return true
|
|
95
|
+
const before = start.children.filter((node) => node !== start.item)
|
|
96
|
+
const after = [...start.from.children].filter((node) => node !== start.item)
|
|
97
|
+
return !sameNodes(before, after)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The drop left the card in the slot it started in: not a move, not an event, not an
|
|
101
|
+
// entry.
|
|
102
|
+
function sameSlot(start, result) {
|
|
103
|
+
if (result.from !== result.to) return false
|
|
104
|
+
const now = [...result.to.children]
|
|
105
|
+
return sameNodes(now, start.children)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function removal(start) {
|
|
109
|
+
const member = (node) => node && node.parentNode === start.from
|
|
110
|
+
const next = member(start.next) ? start.next : null
|
|
111
|
+
return {
|
|
112
|
+
kind: 'remove',
|
|
113
|
+
parent: start.from,
|
|
114
|
+
nodes: [start.item],
|
|
115
|
+
before: next,
|
|
116
|
+
afterAuthored: next,
|
|
117
|
+
beforeAuthored: member(start.previous) ? start.previous : null,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function addition(result) {
|
|
122
|
+
return {
|
|
123
|
+
kind: 'add',
|
|
124
|
+
parent: result.to,
|
|
125
|
+
nodes: [result.item],
|
|
126
|
+
before: result.item.nextSibling,
|
|
127
|
+
afterAuthored: result.item.nextSibling,
|
|
128
|
+
beforeAuthored: result.item.previousSibling,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Record one completed move from the start slot to the final slot. The runtime
|
|
133
|
+
// attributes are already restored by the time this runs, so no vendor write can reach
|
|
134
|
+
// the entry. Returns the engine's receipt, or null when this move earns no entry.
|
|
135
|
+
export function recordMove(start, result) {
|
|
136
|
+
if (!start || !result || result.item !== start.item) return null
|
|
137
|
+
const label = moveLabel(result)
|
|
138
|
+
if (drifted(start, result)) {
|
|
139
|
+
document.dispatchEvent(new CustomEvent('clay:undo-unavailable', {
|
|
140
|
+
detail: { label, code: 'conflicted' },
|
|
141
|
+
}))
|
|
142
|
+
return null
|
|
143
|
+
}
|
|
144
|
+
if (sameSlot(start, result)) return null
|
|
145
|
+
const undo = engine()
|
|
146
|
+
if (!undo?.command || !undo?.record) return null
|
|
147
|
+
holdMove(start)
|
|
148
|
+
return undo.command(label, () => {
|
|
149
|
+
undo.record(removal(start))
|
|
150
|
+
undo.record(addition(result))
|
|
151
|
+
})
|
|
152
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The one BroadcastChannel a document's same-browser tabs share: the durable lane
|
|
2
|
+
// carries the room's frames over it, and the local presence service carries
|
|
3
|
+
// `presence-v1` session frames beside them. This module owns opening the channel,
|
|
4
|
+
// routing a message to the subscribers that asked for its kind, and closing it
|
|
5
|
+
// when the last of them lets go, so neither service opens or closes the other's.
|
|
6
|
+
//
|
|
7
|
+
// The wire version stays the durable lane's 4, and `presence-v1` is an optional
|
|
8
|
+
// message family beside the durable types: a client that does not know it already
|
|
9
|
+
// ignores every type it does not handle.
|
|
10
|
+
|
|
11
|
+
const VERSION = 4;
|
|
12
|
+
export const PRESENCE_TYPE = "presence-v1";
|
|
13
|
+
|
|
14
|
+
export function localChannelSupported() {
|
|
15
|
+
return typeof BroadcastChannel === "function";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A channel handle for one document's tabs. `post` adds the wire version;
|
|
20
|
+
* `subscribe` reports the messages of one kind ("durable" or "presence") and
|
|
21
|
+
* returns the release, which closes the channel once it is the last one out.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} key
|
|
24
|
+
*/
|
|
25
|
+
export function localChannel(key) {
|
|
26
|
+
const channel = new BroadcastChannel(key);
|
|
27
|
+
const listeners = { durable: new Set(), presence: new Set() };
|
|
28
|
+
let refs = 0;
|
|
29
|
+
|
|
30
|
+
channel.onmessage = ({ data }) => {
|
|
31
|
+
if (!data || data.v !== VERSION || typeof data.type !== "string") return;
|
|
32
|
+
const set = data.type === PRESENCE_TYPE ? listeners.presence : listeners.durable;
|
|
33
|
+
for (const listener of [...set]) listener(data);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
key,
|
|
38
|
+
get open() {
|
|
39
|
+
return refs > 0;
|
|
40
|
+
},
|
|
41
|
+
post(message) {
|
|
42
|
+
if (!refs) return;
|
|
43
|
+
channel.postMessage({ v: VERSION, ...message });
|
|
44
|
+
},
|
|
45
|
+
subscribe(kind, listener) {
|
|
46
|
+
const set = kind === "presence" ? listeners.presence : listeners.durable;
|
|
47
|
+
set.add(listener);
|
|
48
|
+
refs += 1;
|
|
49
|
+
let released = false;
|
|
50
|
+
return () => {
|
|
51
|
+
if (released) return;
|
|
52
|
+
released = true;
|
|
53
|
+
set.delete(listener);
|
|
54
|
+
refs -= 1;
|
|
55
|
+
if (refs > 0) return;
|
|
56
|
+
channel.onmessage = null;
|
|
57
|
+
channel.close();
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|