@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/attrs/save-freeze.js
CHANGED
|
@@ -17,13 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { onSnapshot, addDocumentTransform } from "../core/snapshot.js";
|
|
20
|
-
import { isEditMode } from "../core/is-edit-mode.js";
|
|
21
20
|
import { FREEZE_SELECTOR } from "../lib/region-policy.js";
|
|
22
|
-
import {
|
|
23
|
-
import { stripEphemeral } from "../lib/ephemeral.js";
|
|
24
|
-
import Mutation from "../lib/mutation.js";
|
|
25
|
-
|
|
26
|
-
const originals = new WeakMap();
|
|
21
|
+
import { captureAll, frozenOriginal } from "../core/preserve-authored.js";
|
|
27
22
|
|
|
28
23
|
const saveFreeze = { debug: false };
|
|
29
24
|
|
|
@@ -31,32 +26,6 @@ function log(...args) {
|
|
|
31
26
|
if (saveFreeze.debug) console.log('[save-freeze]', ...args);
|
|
32
27
|
}
|
|
33
28
|
|
|
34
|
-
// The captured original is what the file keeps, so it must hold no router stamp
|
|
35
|
-
// and no ephemeral state: either can be set on a child before this capture runs.
|
|
36
|
-
function authoredInnerHTML(el) {
|
|
37
|
-
const html = el.innerHTML;
|
|
38
|
-
if (!el.querySelector(`[${ROUTE_ACTIVE_ATTR}]`) && !html.includes("ephemeral") && !html.includes("<dialog")) return html;
|
|
39
|
-
const copy = el.cloneNode(true);
|
|
40
|
-
for (const s of copy.querySelectorAll(`[${ROUTE_ACTIVE_ATTR}]`)) s.removeAttribute(ROUTE_ACTIVE_ATTR);
|
|
41
|
-
for (const child of copy.children) stripEphemeral(child);
|
|
42
|
-
return copy.innerHTML;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function capture(el) {
|
|
46
|
-
if (!originals.has(el)) {
|
|
47
|
-
originals.set(el, authoredInnerHTML(el));
|
|
48
|
-
log('captured original', originals.get(el).substring(0, 80) + '...');
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function captureAll() {
|
|
53
|
-
const els = document.querySelectorAll(FREEZE_SELECTOR);
|
|
54
|
-
log('captureAll found', els.length, 'elements');
|
|
55
|
-
for (const el of els) {
|
|
56
|
-
capture(el);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
29
|
// Which cloned element holds which authored innerHTML, for the current snapshot.
|
|
61
30
|
// Keyed by the cloned element itself: the prepare phase runs on the same clone
|
|
62
31
|
// object the snapshot phase saw, so identity is the pairing. An attribute marker
|
|
@@ -76,8 +45,9 @@ function freezeSnapshot(clone, provenance) {
|
|
|
76
45
|
|
|
77
46
|
for (const cloneElement of cloneElements) {
|
|
78
47
|
const liveElement = provenance.original(cloneElement);
|
|
79
|
-
if (!liveElement
|
|
80
|
-
const original =
|
|
48
|
+
if (!liveElement) continue;
|
|
49
|
+
const original = frozenOriginal(liveElement);
|
|
50
|
+
if (original === undefined) continue;
|
|
81
51
|
pending.set(cloneElement, original);
|
|
82
52
|
if (cloneElement.innerHTML !== original) {
|
|
83
53
|
log('restoring original');
|
|
@@ -99,27 +69,12 @@ function freezePrepare(clone) {
|
|
|
99
69
|
}
|
|
100
70
|
|
|
101
71
|
function init() {
|
|
102
|
-
log('init
|
|
103
|
-
if (!isEditMode) return;
|
|
72
|
+
log('init');
|
|
104
73
|
|
|
74
|
+
// The always wave's preservation foundation already recorded every region, in view
|
|
75
|
+
// mode and before any paint; this only covers a boot that skipped that wave.
|
|
105
76
|
captureAll();
|
|
106
77
|
|
|
107
|
-
// Capture the original innerHTML of dynamically added [freeze]/[save-freeze]
|
|
108
|
-
// regions. Folded onto the single Mutation observer (Phase 2 — was its own).
|
|
109
|
-
// debounce:0 so we snapshot the authored content before any runtime edit
|
|
110
|
-
// mutates it; the callback is a pure read into a WeakMap (no DOM writes).
|
|
111
|
-
// require:'observed' (skip only no-watch); pausable:false so a morphed-in
|
|
112
|
-
// freeze region is still captured. Mutation observes document.documentElement,
|
|
113
|
-
// so a [freeze] added directly to <head> at runtime is captured like any other.
|
|
114
|
-
Mutation.onAddElement({
|
|
115
|
-
selectorFilter: FREEZE_SELECTOR,
|
|
116
|
-
require: 'observed',
|
|
117
|
-
pausable: false,
|
|
118
|
-
debounce: 0
|
|
119
|
-
}, (changes) => {
|
|
120
|
-
changes.forEach(({ element }) => capture(element));
|
|
121
|
-
});
|
|
122
|
-
|
|
123
78
|
// Phase 2: Freeze in snapshot — before snapshot-ready fires.
|
|
124
79
|
// This prevents live-sync from writing unfrozen content to disk.
|
|
125
80
|
onSnapshot(freezeSnapshot);
|
package/src/core/admin-attrs.js
CHANGED
|
@@ -1,22 +1,129 @@
|
|
|
1
1
|
// Admin system - combines all admin features (edit-only).
|
|
2
|
-
import { init as initContenteditable } from './admin-contenteditable.js';
|
|
3
|
-
import { init as initInputs } from './admin-inputs.js';
|
|
4
|
-
import { init as initOnClick } from './admin-onclick.js';
|
|
5
|
-
import { init as initResources } from './admin-resources.js';
|
|
2
|
+
import { init as initContenteditable, enableContentEditable, disableContentEditable, SELECTOR as CONTENTEDITABLE } from './admin-contenteditable.js';
|
|
3
|
+
import { init as initInputs, enableAdminInputs, disableAdminInputs, SELECTOR_DISABLED, SELECTOR_READONLY } from './admin-inputs.js';
|
|
4
|
+
import { init as initOnClick, enableOnClick, disableOnClick, SELECTOR as ONCLICK } from './admin-onclick.js';
|
|
5
|
+
import { init as initResources, enableAdminResources, disableAdminResources, SELECTOR as RESOURCES } from './admin-resources.js';
|
|
6
|
+
import Mutation from '../lib/mutation.js';
|
|
7
|
+
import { isEditMode } from './is-edit-mode.js';
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
// Every control this family activates, in one selector: what a node added mid-session is
|
|
10
|
+
// matched against, and what the lease enables.
|
|
11
|
+
const SELECTOR = [CONTENTEDITABLE, SELECTOR_DISABLED, SELECTOR_READONLY, ONCLICK, RESOURCES].join(', ');
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
const ENABLE = [enableContentEditable, enableAdminInputs, enableOnClick, enableAdminResources];
|
|
14
|
+
|
|
15
|
+
let installation = null;
|
|
16
|
+
// The one add-element subscription this page runs, and whether it admits new controls.
|
|
17
|
+
// A live entry installs it at its enable and removes it at its dispose; a tab that booted
|
|
18
|
+
// in edit has no lease to do either, so its installation starts it and the adopted
|
|
19
|
+
// session's dispose is what takes it away.
|
|
20
|
+
let watching = null;
|
|
21
|
+
let admission = false;
|
|
22
|
+
|
|
23
|
+
const activate = (node) => {
|
|
24
|
+
const scope = node.parentNode || node;
|
|
25
|
+
for (const enable of ENABLE) enable(scope);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// One shared function, one install, however many times a session asks for it: a control
|
|
29
|
+
// that arrives after the sweep — add-from's clone, a node a live frame morphed in — is
|
|
30
|
+
// activated as it lands, and nothing after the subscription is removed.
|
|
31
|
+
function watch() {
|
|
32
|
+
admission = true;
|
|
33
|
+
if (watching) return;
|
|
34
|
+
watching = Mutation.onAddElement({
|
|
35
|
+
selectorFilter: SELECTOR,
|
|
36
|
+
require: 'observed',
|
|
37
|
+
pausable: false,
|
|
38
|
+
debounce: 0,
|
|
39
|
+
}, (changes) => {
|
|
40
|
+
if (!admission) return;
|
|
41
|
+
changes.forEach(({ element }) => activate(element));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function unwatch() {
|
|
46
|
+
admission = false;
|
|
47
|
+
if (!watching) return;
|
|
48
|
+
watching();
|
|
49
|
+
watching = null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Loader-managed initialization. The loader calls this after it imports the module, in
|
|
53
|
+
// its own wave order; the import calls it too, so a page reaches the same installation by
|
|
54
|
+
// either road. Calling it again is asking for the transforms the page already has, not
|
|
55
|
+
// for a second set: the save-time restore of each authored form is installed once, which
|
|
56
|
+
// is what makes a repeated entry cost nothing.
|
|
57
|
+
export function install() {
|
|
58
|
+
if (installation) return installation;
|
|
12
59
|
initContenteditable();
|
|
13
60
|
initInputs();
|
|
14
61
|
initOnClick();
|
|
15
62
|
initResources();
|
|
63
|
+
installation = { selector: SELECTOR };
|
|
64
|
+
// A tab that boots in edit: the boot's own page-load helpers turned the served controls
|
|
65
|
+
// on and there is no lease yet to watch for the ones that arrive afterwards.
|
|
66
|
+
if (isEditMode) watch();
|
|
67
|
+
return installation;
|
|
16
68
|
}
|
|
17
69
|
|
|
18
|
-
|
|
19
|
-
|
|
70
|
+
install();
|
|
71
|
+
|
|
72
|
+
// One edit session over the live DOM. `enable` turns the served inert controls into
|
|
73
|
+
// working ones and starts watching for controls that arrive afterwards — a clone from
|
|
74
|
+
// add-from, a node a live frame morphed in; `quiesce` leaves them exactly as they are and
|
|
75
|
+
// stops admitting new ones; `resume` is the refused exit; `dispose` hands every control
|
|
76
|
+
// back the form the file served and removes this session's subscription, and nothing else.
|
|
77
|
+
export function controlsLease(root = document) {
|
|
78
|
+
return {
|
|
79
|
+
enable() {
|
|
80
|
+
for (const enable of ENABLE) enable(root);
|
|
81
|
+
watch();
|
|
82
|
+
},
|
|
83
|
+
quiesce() {
|
|
84
|
+
admission = false;
|
|
85
|
+
},
|
|
86
|
+
resume() {
|
|
87
|
+
admission = true;
|
|
88
|
+
},
|
|
89
|
+
dispose() {
|
|
90
|
+
unwatch();
|
|
91
|
+
disableContentEditable();
|
|
92
|
+
disableAdminInputs();
|
|
93
|
+
disableOnClick();
|
|
94
|
+
disableAdminResources();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The controls as a mode participant.
|
|
101
|
+
*
|
|
102
|
+
* The installation above is page-lifetime and turns no control on: it puts the
|
|
103
|
+
* save-time restore in front of each authored form, and in a tab that booted in edit it
|
|
104
|
+
* starts the one watcher. A session is the lease below.
|
|
105
|
+
*
|
|
106
|
+
* `initial: true` adopts the edit this tab booted into, where the boot's own
|
|
107
|
+
* `enable...OnPageLoad` helpers already turned the served controls on: its enable asks
|
|
108
|
+
* for the same one watcher the installation already started, and its dispose is what
|
|
109
|
+
* hands the controls back.
|
|
110
|
+
*/
|
|
111
|
+
export function modeParticipant() {
|
|
112
|
+
return {
|
|
113
|
+
id: 'clay:controls',
|
|
114
|
+
order: 110,
|
|
115
|
+
prepare(context = {}) {
|
|
116
|
+
const lease = controlsLease();
|
|
117
|
+
if (context.initial !== true) return lease;
|
|
118
|
+
return {
|
|
119
|
+
enable: () => watch(),
|
|
120
|
+
quiesce: () => lease.quiesce(),
|
|
121
|
+
resume: () => lease.resume(),
|
|
122
|
+
dispose: () => lease.dispose(),
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
20
127
|
|
|
21
|
-
export { init };
|
|
22
|
-
export default
|
|
128
|
+
export { install as init };
|
|
129
|
+
export default install;
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { isEditMode, isOwner } from "./is-edit-mode.js";
|
|
2
2
|
import onDomReady from "../lib/dom-ready.js";
|
|
3
3
|
import { addDocumentTransform } from "./snapshot.js";
|
|
4
|
+
import { authoredAttr, canonicalContentEditable, captureAuthored, restoreAuthoredOrder } from "./preserve-authored.js";
|
|
4
5
|
|
|
5
6
|
export const SELECTOR = '[editmode\\:contenteditable]';
|
|
6
7
|
|
|
7
8
|
export function disableContentEditableBeforeSave () {
|
|
8
9
|
addDocumentTransform(docElem => {
|
|
9
|
-
docElem.querySelectorAll(SELECTOR).forEach(
|
|
10
|
-
const originalValue = resource.getAttribute("contenteditable");
|
|
11
|
-
resource.setAttribute("inert-contenteditable", originalValue === null ? "true" : originalValue);
|
|
12
|
-
resource.removeAttribute("contenteditable");
|
|
13
|
-
});
|
|
10
|
+
docElem.querySelectorAll(SELECTOR).forEach(canonicalContentEditable);
|
|
14
11
|
});
|
|
15
12
|
}
|
|
16
13
|
|
|
@@ -26,10 +23,16 @@ export function enableContentEditableForAdminOnPageLoad () {
|
|
|
26
23
|
// incoming document the same way boot activates the live one.
|
|
27
24
|
export function enableContentEditable(root = document) {
|
|
28
25
|
root.querySelectorAll(SELECTOR).forEach(el => {
|
|
29
|
-
|
|
26
|
+
captureAuthored(el);
|
|
27
|
+
// Already active (a second activation of the same node) keeps the value it already
|
|
28
|
+
// holds, so re-running this never downgrades `plaintext-only` to `true`.
|
|
29
|
+
const val = el.getAttribute("inert-contenteditable")
|
|
30
|
+
?? el.getAttribute("contenteditable")
|
|
31
|
+
?? authoredAttr(el, "inert-contenteditable")
|
|
32
|
+
?? "true";
|
|
30
33
|
// Keywords match ASCII case-insensitively, so `FALSE` means false. The author's own
|
|
31
34
|
// spelling is what gets written, so the save transform puts it back unchanged.
|
|
32
|
-
const keyword = val
|
|
35
|
+
const keyword = val.toLowerCase();
|
|
33
36
|
el.setAttribute("contenteditable", ["", "true", "false", "plaintext-only"].includes(keyword) ? val : "true");
|
|
34
37
|
el.removeAttribute("inert-contenteditable");
|
|
35
38
|
});
|
|
@@ -37,9 +40,8 @@ export function enableContentEditable(root = document) {
|
|
|
37
40
|
|
|
38
41
|
export function disableContentEditable() {
|
|
39
42
|
document.querySelectorAll(SELECTOR).forEach(el => {
|
|
40
|
-
|
|
41
|
-
el
|
|
42
|
-
el.removeAttribute("contenteditable");
|
|
43
|
+
canonicalContentEditable(el);
|
|
44
|
+
restoreAuthoredOrder(el);
|
|
43
45
|
});
|
|
44
46
|
}
|
|
45
47
|
|
package/src/core/admin-inputs.js
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { isEditMode, isOwner } from "./is-edit-mode.js";
|
|
2
2
|
import onDomReady from "../lib/dom-ready.js";
|
|
3
3
|
import { addDocumentTransform, originalSnapshotNode } from "./snapshot.js";
|
|
4
|
+
import { authoredAttr, captureAuthored, restoreAuthoredOrder } from "./preserve-authored.js";
|
|
4
5
|
|
|
5
6
|
export const SELECTOR_DISABLED = '[viewmode\\:disabled]';
|
|
6
7
|
export const SELECTOR_READONLY = '[viewmode\\:readonly]';
|
|
7
8
|
|
|
8
9
|
const BOOLEANS = [[SELECTOR_DISABLED, 'disabled'], [SELECTOR_READONLY, 'readonly']];
|
|
9
10
|
|
|
10
|
-
// What the author wrote for `disabled` / `readonly` on each live element, from before
|
|
11
|
-
// edit mode removed it. A boolean attribute means the same whatever its value, so this
|
|
12
|
-
// is only about handing the file back its own spelling (`disabled="disabled"`).
|
|
13
|
-
const authored = new WeakMap();
|
|
14
|
-
|
|
15
11
|
export function disableAdminInputsBeforeSave() {
|
|
16
12
|
addDocumentTransform(docElem => {
|
|
17
13
|
for (const [selector, name] of BOOLEANS) {
|
|
18
14
|
docElem.querySelectorAll(selector).forEach(input => {
|
|
19
15
|
if (input.hasAttribute(name)) return;
|
|
20
16
|
const live = originalSnapshotNode(input);
|
|
21
|
-
const spelled = live
|
|
22
|
-
input.setAttribute(name, spelled
|
|
17
|
+
const spelled = live ? authoredAttr(live, name) : null;
|
|
18
|
+
input.setAttribute(name, spelled === null ? '' : spelled);
|
|
23
19
|
});
|
|
24
20
|
}
|
|
25
21
|
});
|
|
@@ -38,20 +34,23 @@ export function enableAdminInputsOnPageLoad() {
|
|
|
38
34
|
export function enableAdminInputs(root = document) {
|
|
39
35
|
for (const [selector, name] of BOOLEANS) {
|
|
40
36
|
root.querySelectorAll(selector).forEach(input => {
|
|
41
|
-
|
|
42
|
-
if (value !== null) authored.set(input, { ...(authored.get(input) || {}), [name]: value });
|
|
37
|
+
captureAuthored(input);
|
|
43
38
|
input.removeAttribute(name);
|
|
44
39
|
});
|
|
45
40
|
}
|
|
46
41
|
}
|
|
47
42
|
|
|
43
|
+
// A boolean attribute means the same whatever its value, so the point of putting it
|
|
44
|
+
// back is handing the file its own spelling (`disabled="disabled"`) instead of `""`.
|
|
48
45
|
export function disableAdminInputs() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
for (const [selector, name] of BOOLEANS) {
|
|
47
|
+
document.querySelectorAll(selector).forEach(input => {
|
|
48
|
+
const spelled = authoredAttr(input, name);
|
|
49
|
+
if (spelled === null) input.removeAttribute(name);
|
|
50
|
+
else input.setAttribute(name, spelled);
|
|
51
|
+
restoreAuthoredOrder(input);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
export function init() {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isEditMode, isOwner } from "./is-edit-mode.js";
|
|
2
2
|
import onDomReady from "../lib/dom-ready.js";
|
|
3
3
|
import { addDocumentTransform } from "./snapshot.js";
|
|
4
|
+
import { restoreAuthoredOrder } from "./preserve-authored.js";
|
|
4
5
|
|
|
5
6
|
export const SELECTOR = '[editmode\\:onclick]';
|
|
6
7
|
|
|
@@ -46,6 +47,7 @@ export function disableOnClick() {
|
|
|
46
47
|
el.setAttribute("inert-onclick", val);
|
|
47
48
|
el.removeAttribute("onclick");
|
|
48
49
|
}
|
|
50
|
+
restoreAuthoredOrder(el);
|
|
49
51
|
});
|
|
50
52
|
}
|
|
51
53
|
|
package/src/core/autosave.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import Mutation from "../lib/mutation.js";
|
|
14
14
|
import { isEditMode } from "./is-edit-mode.js";
|
|
15
|
-
import {
|
|
15
|
+
import { autosaveScheduler, savesAdmitted } from "./save.js";
|
|
16
16
|
import { markUserDriven } from "../lib/user-gesture.js";
|
|
17
17
|
import { resolveRegionPolicy, skipForPolicy } from "../lib/region-policy.js";
|
|
18
18
|
import { PERSIST, AUTOSAVE } from "../lib/attr-aliases.js";
|
|
@@ -20,25 +20,20 @@ import { setAutosaveActive } from "../lib/autosave-state.js";
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Initialize auto-save on DOM changes
|
|
23
|
-
*
|
|
23
|
+
*
|
|
24
|
+
* The scheduler owns the quiet period and the maximum wait, so this subscriber
|
|
25
|
+
* only reports that content changed. No debounce here: the hub's would be a
|
|
26
|
+
* second timer measuring the same quiet window, and the page must not be copied
|
|
27
|
+
* to answer a question the scheduler has not yet admitted.
|
|
24
28
|
*/
|
|
25
|
-
// A bare debounce resets on every change, so a page with anything that mutates
|
|
26
|
-
// faster than the delay — a clock, a countdown, a polling counter — pushes autosave
|
|
27
|
-
// into the future forever and it never fires, with nothing in the UI saying so.
|
|
28
|
-
// maxWait bounds that: however long the churn lasts, a save happens within
|
|
29
|
-
// AUTOSAVE_MAX_WAIT_MS of the first change it was waiting on. (The settled baseline
|
|
30
|
-
// capture in save.js already solved this for itself with MAX_SETTLE_MS.)
|
|
31
|
-
const AUTOSAVE_DEBOUNCE_MS = 1500;
|
|
32
|
-
const AUTOSAVE_MAX_WAIT_MS = 10000;
|
|
33
|
-
|
|
34
29
|
function initSavePageOnChange() {
|
|
35
30
|
Mutation.onAnyChange({
|
|
36
|
-
debounce:
|
|
37
|
-
maxWait: AUTOSAVE_MAX_WAIT_MS,
|
|
31
|
+
debounce: 0,
|
|
38
32
|
omitChangeDetails: true,
|
|
39
33
|
require: 'autosave'
|
|
40
34
|
}, () => {
|
|
41
|
-
|
|
35
|
+
if (!savesAdmitted()) return;
|
|
36
|
+
autosaveScheduler.noteActivity();
|
|
42
37
|
});
|
|
43
38
|
}
|
|
44
39
|
|
|
@@ -46,7 +41,6 @@ function initSavePageOnChange() {
|
|
|
46
41
|
* Initialize auto-save on input events for [persist] elements
|
|
47
42
|
* Form input values don't trigger DOM mutations, so we listen for input events
|
|
48
43
|
*/
|
|
49
|
-
let inputSaveTimer = null;
|
|
50
44
|
function initSaveOnPersistInput() {
|
|
51
45
|
document.addEventListener('input', (e) => {
|
|
52
46
|
if (!e.target.closest(PERSIST)) return;
|
|
@@ -58,24 +52,83 @@ function initSaveOnPersistInput() {
|
|
|
58
52
|
if (e.isTrusted && !skipForPolicy(resolveRegionPolicy(e.target), 'dirty')) {
|
|
59
53
|
markUserDriven();
|
|
60
54
|
}
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
if (!savesAdmitted()) return;
|
|
56
|
+
autosaveScheduler.noteActivity();
|
|
63
57
|
}, true);
|
|
64
58
|
}
|
|
65
59
|
|
|
66
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Record editable input and composition activity while an automatic save is
|
|
62
|
+
* pending.
|
|
63
|
+
*
|
|
64
|
+
* An active IME changes nothing in the canonical record yet, so a pause between
|
|
65
|
+
* composition updates would read as quiet and the capture would land mid-word.
|
|
66
|
+
* This records timing only: it makes no input dirty, so a frozen editor and a
|
|
67
|
+
* no-save field still save nothing.
|
|
68
|
+
*/
|
|
69
|
+
function initRecordEditActivity() {
|
|
70
|
+
const noteActivity = (e) => {
|
|
71
|
+
if (!autosaveScheduler.pending()) return;
|
|
72
|
+
const target = e.target;
|
|
73
|
+
if (!target || typeof target.closest !== 'function') return;
|
|
74
|
+
const field = target.closest('input, textarea, select, [contenteditable]');
|
|
75
|
+
if (!field) return;
|
|
76
|
+
if (skipForPolicy(resolveRegionPolicy(field), 'dirty')) return;
|
|
77
|
+
if (!savesAdmitted()) return;
|
|
78
|
+
autosaveScheduler.noteActivity();
|
|
79
|
+
};
|
|
80
|
+
document.addEventListener('input', noteActivity, true);
|
|
81
|
+
document.addEventListener('compositionupdate', noteActivity, true);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let installed = false;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Loader-managed initialization, and the import's own: one subscription set per page.
|
|
88
|
+
*
|
|
89
|
+
* A live re-entry asks for the autosave this page already has, not a second one, so the
|
|
90
|
+
* listeners above are installed once and an activity report they make while a save
|
|
91
|
+
* session has admission closed is dropped rather than arming another timer.
|
|
92
|
+
*/
|
|
93
|
+
function install() {
|
|
94
|
+
if (installed) return;
|
|
67
95
|
if (!document.documentElement.matches(AUTOSAVE)) return;
|
|
68
96
|
if (!isEditMode) return;
|
|
97
|
+
installed = true;
|
|
69
98
|
setAutosaveActive(true);
|
|
70
99
|
// initUserGesture moved to save.js's init: gesture provenance belongs to every
|
|
71
100
|
// editable page, not only the ones with <html autosave>.
|
|
72
101
|
initSavePageOnChange();
|
|
73
102
|
initSaveOnPersistInput();
|
|
103
|
+
initRecordEditActivity();
|
|
74
104
|
}
|
|
75
105
|
|
|
76
|
-
// No window exports -
|
|
106
|
+
// No window exports - the scheduler lives in save-system and savePageThrottled is
|
|
107
|
+
// exported from there
|
|
77
108
|
|
|
78
109
|
// Auto-init when module is imported
|
|
79
|
-
|
|
110
|
+
install();
|
|
111
|
+
|
|
112
|
+
export { install as init };
|
|
113
|
+
export default install;
|
|
80
114
|
|
|
81
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Auto-save as a mode participant.
|
|
117
|
+
*
|
|
118
|
+
* A live entry imports this module in view, where `install` above is inert, so the
|
|
119
|
+
* participant is how the entry asks for it once the mode has committed. Nothing here
|
|
120
|
+
* belongs to a session: the scheduler already parks an attempt a closed save session
|
|
121
|
+
* refuses (`savePageThrottled`) and replays it on `resume`, and every attempt checks the
|
|
122
|
+
* mode, so quiesce and dispose have nothing to undo. `initial: true` adopts the edit this
|
|
123
|
+
* tab booted into, where the import's own `install` already ran: the lease starts nothing.
|
|
124
|
+
*/
|
|
125
|
+
export function modeParticipant() {
|
|
126
|
+
return {
|
|
127
|
+
id: "clay:autosave",
|
|
128
|
+
order: 20,
|
|
129
|
+
prepare(context = {}) {
|
|
130
|
+
if (context.initial === true) return { enable() {}, quiesce() {}, resume() {}, dispose() {} };
|
|
131
|
+
return { enable: install, quiesce() {}, resume() {}, dispose() {} };
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -89,6 +89,28 @@ export function downloadHtml({ html, name }) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// The review panel's own copy: the page a reload kept, when there is one, with every
|
|
93
|
+
// stored row it drew listed beside it as JSON. The rows are already plain, so nothing
|
|
94
|
+
// here reads the ledger the tab no longer has.
|
|
95
|
+
export function downloadStored({ html = null, edits = [] } = {}) {
|
|
96
|
+
try {
|
|
97
|
+
const page = typeof html === "string" ? html : captureRecoveryPage();
|
|
98
|
+
const data = {
|
|
99
|
+
format: "clay-lost-edits",
|
|
100
|
+
version: 1,
|
|
101
|
+
savedAt: new Date().toISOString(),
|
|
102
|
+
page: location.href,
|
|
103
|
+
refused: false,
|
|
104
|
+
edits: Array.isArray(edits) ? edits : [],
|
|
105
|
+
earlierPages: [],
|
|
106
|
+
};
|
|
107
|
+
return downloadHtml({ html: embed(page, data), name: `${documentName()}-my-copy-${stamp()}.html` });
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error("clayjs: could not build the download", err);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
92
114
|
export function downloadRecovery(records, { refused = false } = {}) {
|
|
93
115
|
try {
|
|
94
116
|
const { html, name } = buildRecovery(records, { refused });
|