@panphora/clayjs 1.15.2 → 1.17.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 +84291 -57611
- 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-ZUE7PSCI.js +1 -0
- package/dist/m/attrs/save-freeze-MYNVXREN.js +1 -0
- package/dist/m/c-2AZDZWCK.js +1 -0
- package/dist/m/c-2SPVQSHC.js +1 -0
- package/dist/m/c-2YYDMLNZ.js +1 -0
- package/dist/m/c-336SGU5W.js +1 -0
- package/dist/m/c-33JJ6CBX.js +2 -0
- package/dist/m/c-3GJ4SDE2.js +1 -0
- package/dist/m/c-3H6BZDNJ.js +6 -0
- package/dist/m/c-3OWDMNJR.js +2 -0
- package/dist/m/c-3RUU4TPX.js +91 -0
- package/dist/m/c-3XIRD4NU.js +1 -0
- package/dist/m/c-46XYM55L.js +1 -0
- package/dist/m/c-4DFEJ2PO.js +3 -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-56QRZTZH.js +1 -0
- package/dist/m/c-5J4E7O4Y.js +4 -0
- package/dist/m/c-5LKDWQTL.js +19 -0
- package/dist/m/c-5QWQ7A5L.js +1 -0
- package/dist/m/c-5RJFTLSF.js +1 -0
- package/dist/m/c-5V3MOVJT.js +1 -0
- package/dist/m/c-5VYOWDTI.js +1 -0
- package/dist/m/c-5YIHJ3TJ.js +1 -0
- package/dist/m/c-6AZOGAPE.js +1 -0
- package/dist/m/c-6CRUUXOX.js +5 -0
- package/dist/m/c-6HMB24EV.js +1 -0
- package/dist/m/c-6KEVEQ7L.js +1 -0
- package/dist/m/c-6NC5I2TV.js +1 -0
- package/dist/m/c-74MGYWZF.js +1 -0
- package/dist/m/c-7DJDKPGL.js +1 -0
- package/dist/m/c-7OLLJPWP.js +1 -0
- package/dist/m/c-7SWMYKAI.js +1 -0
- package/dist/m/c-A3E3NIMC.js +1 -0
- package/dist/m/c-A7LS5UZW.js +1 -0
- package/dist/m/c-AMSNPOCX.js +1 -0
- package/dist/m/c-AQLFEATB.js +1 -0
- package/dist/m/c-B3JM5ZXC.js +1 -0
- package/dist/m/c-BBVGTQG7.js +1 -0
- package/dist/m/c-BPF2NSPV.js +1 -0
- package/dist/m/c-BQ6LOBEE.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-CJ3A7ZKE.js +1 -0
- package/dist/m/c-CQHPGKX2.js +1 -0
- package/dist/m/c-CYWLPPUS.js +1 -0
- package/dist/m/c-CZUSOB7V.js +1 -0
- package/dist/m/c-DCFSEJZG.js +1 -0
- package/dist/m/c-DCXOXBF3.js +1 -0
- package/dist/m/c-DFS4XZJV.js +2 -0
- package/dist/m/c-DNENWY4O.js +1 -0
- package/dist/m/c-DNZ5IFM6.js +1 -0
- package/dist/m/c-E4LIR5X5.js +1 -0
- package/dist/m/c-EJTGYSLN.js +1 -0
- package/dist/m/c-EVCQ3BKA.js +1 -0
- package/dist/m/c-EZU56NQV.js +34 -0
- package/dist/m/c-FDNHHZY5.js +1 -0
- package/dist/m/c-FILLTJA7.js +1 -0
- package/dist/m/c-FRLHSY7E.js +1 -0
- package/dist/m/c-FROJUZMV.js +1 -0
- package/dist/m/c-G2JPKCTP.js +1 -0
- package/dist/m/c-GB6TU7WV.js +1 -0
- package/dist/m/c-GFJJEBTU.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-GSFIGKOP.js +1 -0
- package/dist/m/c-GY422N3Q.js +1 -0
- package/dist/m/c-H4LG442Q.js +1 -0
- package/dist/m/c-HA6INT6J.js +1 -0
- package/dist/m/c-HAWFNBYG.js +1 -0
- package/dist/m/c-HDDEEL77.js +1 -0
- package/dist/m/c-HFKUVACH.js +1 -0
- package/dist/m/c-HHDFCNHV.js +1 -0
- package/dist/m/c-HPURYVZS.js +2 -0
- package/dist/m/c-I2ZATQCD.js +2 -0
- package/dist/m/c-IEM4NTCS.js +1 -0
- package/dist/m/c-IJUK2GPW.js +1 -0
- package/dist/m/c-IW6GGOU2.js +1 -0
- package/dist/m/c-JAX2667S.js +6 -0
- package/dist/m/c-JDCX26UP.js +25 -0
- package/dist/m/c-JRKY44DK.js +1 -0
- package/dist/m/c-JU3NK25H.js +1 -0
- package/dist/m/c-KAU6E43D.js +1 -0
- package/dist/m/c-KBS3UV6B.js +1 -0
- package/dist/m/c-KGVGROZT.js +1 -0
- package/dist/m/c-KI7M4AY3.js +1 -0
- package/dist/m/c-KIPQJX4B.js +1 -0
- package/dist/m/c-KQ6TYJAP.js +135 -0
- package/dist/m/c-KU3ZFADW.js +1 -0
- package/dist/m/c-L4WQSC27.js +1 -0
- package/dist/m/c-LFPHCXY3.js +1 -0
- package/dist/m/c-LRPUO2MX.js +1 -0
- package/dist/m/c-LWLBHLVC.js +3 -0
- package/dist/m/c-LZJPNR2W.js +1 -0
- package/dist/m/c-M2L47S2X.js +7 -0
- package/dist/m/c-MFP2ZWD4.js +1 -0
- package/dist/m/c-MKSQ4Y57.js +1 -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-NRPJEVJS.js +1 -0
- package/dist/m/c-NUFFJVVJ.js +1 -0
- package/dist/m/c-NUHP2XDB.js +1 -0
- package/dist/m/c-NUHSPT5P.js +1 -0
- package/dist/m/c-O72HVYSA.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-OW5QIGG3.js +1 -0
- package/dist/m/c-P7KNDG34.js +1 -0
- package/dist/m/c-PGRCK73F.js +1 -0
- package/dist/m/c-PQ5LK6QE.js +993 -0
- package/dist/m/c-PS5ARVWR.js +9 -0
- package/dist/m/c-PZ4XEPUP.js +2 -0
- package/dist/m/c-Q622NS2Y.js +1 -0
- package/dist/m/c-Q7NQD65V.js +1 -0
- package/dist/m/c-QH63LQOS.js +1 -0
- package/dist/m/c-QHIWXHRO.js +1 -0
- package/dist/m/c-QHWJ5ZIJ.js +1 -0
- package/dist/m/c-QICP2YR5.js +1 -0
- package/dist/m/c-QJ4OM3G6.js +1 -0
- package/dist/m/c-QNKREOL6.js +1 -0
- package/dist/m/c-R3L7ZJGB.js +2 -0
- package/dist/m/c-R7IWVINQ.js +1 -0
- package/dist/m/c-RM7J7BV2.js +1 -0
- package/dist/m/c-RSMOODDU.js +1 -0
- package/dist/m/c-RVMWGXEJ.js +1 -0
- package/dist/m/c-RYUXUUBX.js +1 -0
- package/dist/m/c-S4ZEH46B.js +9 -0
- package/dist/m/c-S7H2DRCB.js +1 -0
- package/dist/m/c-S7LK4KUE.js +1 -0
- package/dist/m/c-SDAXDM2D.js +1 -0
- package/dist/m/c-SGIA4IEQ.js +188 -0
- package/dist/m/c-SMBZG5X4.js +1 -0
- package/dist/m/c-SMGU7RT3.js +235 -0
- package/dist/m/c-SPIORRYS.js +1 -0
- package/dist/m/c-SWLRT66R.js +1 -0
- package/dist/m/c-SZGYW5HT.js +1 -0
- package/dist/m/c-TCCA5BZN.js +2 -0
- package/dist/m/c-TCJTVAFN.js +1 -0
- package/dist/m/c-TJ6WOJL3.js +67 -0
- package/dist/m/c-TQEMVJCS.js +1 -0
- package/dist/m/c-TRS6TM6O.js +1 -0
- package/dist/m/c-TYLXX4NF.js +21 -0
- package/dist/m/c-U62OB4BT.js +1 -0
- package/dist/m/c-U6YWUAR3.js +8 -0
- package/dist/m/c-UDCSU5G5.js +2 -0
- package/dist/m/c-UDUUJPFZ.js +1 -0
- package/dist/m/c-UGHH4MB3.js +1 -0
- package/dist/m/c-UL2YUY52.js +1 -0
- package/dist/m/c-VAK7SG37.js +1 -0
- package/dist/m/c-VDSS4K3Z.js +1 -0
- package/dist/m/c-VE6ZMYMC.js +1 -0
- package/dist/m/c-VG5XIN4T.js +1 -0
- package/dist/m/c-VHM5XQHK.js +1 -0
- package/dist/m/c-VHNSBD2M.js +1 -0
- package/dist/m/c-VI5RMSVY.js +1 -0
- package/dist/m/c-VKEDWYDF.js +1 -0
- package/dist/m/c-VRZN56XY.js +1 -0
- package/dist/m/c-W5MCHEGA.js +1 -0
- package/dist/m/c-WDC5GVKU.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-WWXIDH4O.js +28 -0
- package/dist/m/c-WYFDLITL.js +1 -0
- package/dist/m/c-XCNTK76U.js +1 -0
- package/dist/m/c-XMS6DPBR.js +1 -0
- package/dist/m/c-XN6MBKI6.js +67 -0
- package/dist/m/c-XPELB6E5.js +1 -0
- package/dist/m/c-XQXZAO7N.js +1 -0
- package/dist/m/c-Y5HBBIQ4.js +1 -0
- package/dist/m/c-ZE46QDIH.js +1 -0
- package/dist/m/c-ZUCOKOHT.js +1 -0
- package/dist/m/c-ZVPVXTJF.js +4 -0
- package/dist/m/c-ZVRKXKM7.js +1 -0
- package/dist/m/c-ZWIOJGMT.js +1 -0
- package/dist/m/core/admin-attrs-QHOKPJ7W.js +1 -0
- package/dist/m/core/autosave-HRYGUWV6.js +1 -0
- package/dist/m/core/conflict-notice-6MROJN2L.js +1 -0
- package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
- package/dist/m/core/host-permission-FNXVEFNE.js +1 -0
- package/dist/m/core/is-edit-mode-KTWA3O5N.js +1 -0
- package/dist/m/core/persist-JFYRHMRJ.js +3 -0
- package/dist/m/core/recovery-FLTCC2OC.js +1 -0
- package/dist/m/core/save-HQZ4S667.js +1 -0
- package/dist/m/core/save-core-KGQWFUNH.js +1 -0
- package/dist/m/core/snapshot-EGQZNT45.js +1 -0
- package/dist/m/core/unsaved-warning-IDJLD66H.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-RN2JNGBV.js +1 -0
- package/dist/m/internals/index-PVXGPUDA.js +1 -0
- package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
- package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
- package/dist/m/loader-S4I7GKXR.js +1 -0
- package/dist/m/manifest-4C419D67.js +1 -0
- package/dist/m/manifest-edit-B077F288.js +1 -0
- package/dist/m/manifest.json +3988 -0
- package/dist/m/options/options-EJMS3K24.js +1 -0
- package/dist/m/page/editor-NNJA4OKD.js +202 -0
- package/dist/m/plugins/ai-edit-QN4PMMNL.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-RM5IR6AO.js +1 -0
- package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
- package/dist/m/plugins/indicator-URDBBOQO.js +1 -0
- package/dist/m/plugins/live-HFWD4OIH.js +1 -0
- package/dist/m/plugins/menu-3HIEQSI6.js +138 -0
- package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
- package/dist/m/plugins/page-JOKB6XPR.js +2 -0
- package/dist/m/plugins/page-view-NXOJHNW6.js +1 -0
- package/dist/m/plugins/people-SGPJKANK.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-NRQDSWX6.js +517 -0
- package/dist/m/plugins/richclay-NRQDSWX6.js.LEGAL.txt +1 -0
- package/dist/m/plugins/sortable-GWT4PWDV.js +1 -0
- package/dist/m/plugins/source-4ZRSAIYM.js +1 -0
- package/dist/m/plugins/undo-VDA5P7FH.js +1 -0
- package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
- package/dist/m/plugins/upgrade-W7FG5VFG.js +1 -0
- package/dist/m/plugins/upload-WLYKK6TL.js +1 -0
- package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +21 -0
- package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +1 -0
- package/dist/m/plugins/wire-4N6GYAF6.js +1 -0
- package/dist/m/reader-core-DXYCURFF.js +68 -0
- package/dist/m/route/route-KOCZSEGN.js +1 -0
- package/dist/m/sync/live-sync-J7VMYTKS.js +1 -0
- package/dist/m/ui/index-3JLVI27O.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 +4034 -0
- package/dist/page/themes/light-1.css +430 -0
- package/dist/server/core.cjs +23748 -22855
- 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/boot-phase.js +2 -1
- package/src/core/conflict-download.js +19 -23
- package/src/core/conflict-notice-ui.js +1173 -0
- package/src/core/conflict-notice.js +140 -697
- package/src/core/download.js +32 -0
- package/src/core/edit-mode.js +147 -14
- package/src/core/etag.js +30 -12
- package/src/core/host-meta.js +90 -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 +113 -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 +806 -119
- package/src/core/snapshot.js +159 -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 +140 -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 +113 -6
- 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 +3 -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/chart-tables.js +11 -0
- package/src/page/chart.js +6 -10
- package/src/page/checklist.js +1 -1
- package/src/page/checkpoint-proof.js +289 -0
- package/src/page/chrome.js +431 -63
- package/src/page/csv-import.js +17 -2
- package/src/page/document-title-dom.js +52 -0
- package/src/page/document-title-runtime.js +90 -0
- package/src/page/document-title.js +12 -0
- package/src/page/drag.js +48 -13
- package/src/page/drop.js +2 -1
- package/src/page/editor.js +399 -90
- package/src/page/emoji-data.js +162 -0
- package/src/page/feature-loader.js +72 -0
- package/src/page/features/board.js +21 -0
- package/src/page/features/chart.js +11 -0
- package/src/page/features/code.js +13 -0
- package/src/page/features/dates.js +180 -0
- package/src/page/features/emoji.js +187 -0
- package/src/page/features/media.js +290 -0
- package/src/page/features/mentions.js +188 -0
- package/src/page/features/registry.js +28 -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-command.js +9 -0
- package/src/page/format-gate.js +179 -0
- package/src/page/format-host.js +54 -0
- package/src/page/format-level.js +157 -0
- package/src/page/format-native-commands.js +142 -0
- package/src/page/format-native.js +42 -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 +45 -28
- package/src/page/items.js +67 -14
- package/src/page/link.js +1 -0
- package/src/page/markdown.js +112 -3
- package/src/page/media-views.js +3 -0
- package/src/page/media.js +33 -245
- package/src/page/mention-picker.js +99 -0
- package/src/page/merge.js +1 -1
- package/src/page/outline-cache.js +11 -0
- package/src/page/placeholder.js +27 -7
- package/src/page/projection.js +240 -51
- 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 +46 -4
- package/src/page/save-states.js +1 -1
- package/src/page/schema.js +47 -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/content.css +25 -1
- package/src/page/themes/light-1.css +163 -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-hooks.js +32 -0
- package/src/page/view.js +390 -107
- package/src/page/ybind/awareness.js +172 -0
- package/src/page/ybind/binding.js +40 -5
- 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 +26 -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 +97 -24
- package/src/page/ybind/recovery-proof.js +191 -0
- package/src/page/ybind/recovery-replay.js +488 -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 +319 -35
- 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 +1131 -270
- 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/intro.js +2 -1
- 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 +544 -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/loader.js
CHANGED
|
@@ -1,22 +1,67 @@
|
|
|
1
|
-
import { resolveModules, MODULES } from "./loader-logic.js";
|
|
1
|
+
import { resolveModules, MODULES, PLUGIN_PATHS } from "./loader-logic.js";
|
|
2
2
|
import onDomReady from "./lib/dom-ready.js";
|
|
3
|
-
import { initTheme } from "./ui/theme.js";
|
|
3
|
+
import { initTheme } from "./ui/theme-config.js";
|
|
4
4
|
import { ephemeral } from "./lib/ephemeral.js";
|
|
5
|
+
import { storage } from "./lib/doc-storage.js";
|
|
6
|
+
import { trash } from "./lib/trash.js";
|
|
5
7
|
import { effect } from "./lib/effect.js";
|
|
6
8
|
import { style } from "./lib/style.js";
|
|
7
9
|
import { derived } from "./lib/derived.js";
|
|
10
|
+
import { assets } from "./lib/assets.js";
|
|
8
11
|
import { bootPhase } from "./core/boot-phase.js";
|
|
12
|
+
import { mergeEngine } from "./sync/merge-engine.js";
|
|
13
|
+
import { mergeManifests, preloadEntries } from "./lib/preload.js";
|
|
14
|
+
|
|
15
|
+
// The plugin name a late path announces: the loader's own name-to-path table,
|
|
16
|
+
// read the other way.
|
|
17
|
+
const PLUGIN_NAMES = Object.fromEntries(
|
|
18
|
+
Object.entries(PLUGIN_PATHS).flatMap(([name, spec]) =>
|
|
19
|
+
spec.ownerPath ? [[spec.path, name], [spec.ownerPath, name]] : [[spec.path, name]]
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Every namespace this page imported, boot's plan and a live entry's alike: one record,
|
|
24
|
+
// so an entry skips what boot has and the edit members find their modules.
|
|
25
|
+
const loaded = {};
|
|
26
|
+
|
|
27
|
+
// clay:plugin-ready fires at most once per plugin per page: boot's late tail and a live
|
|
28
|
+
// entry can both reach the same module.
|
|
29
|
+
const announced = new Set();
|
|
30
|
+
|
|
31
|
+
function announce(path) {
|
|
32
|
+
if (announced.has(path)) return;
|
|
33
|
+
announced.add(path);
|
|
34
|
+
document.dispatchEvent(new CustomEvent("clay:plugin-ready", { detail: { plugin: PLUGIN_NAMES[path] } }));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A live entry's hint fetch: the bootstrap's cached one, preloading the plan the entry will
|
|
38
|
+
// import. It lives here, beside the helper, because one more static edge into the reader's
|
|
39
|
+
// graph is one more chunk of every reader's closure.
|
|
40
|
+
const editHints = (loadEditHints, manifest, plan, hasSapJs) => () =>
|
|
41
|
+
loadEditHints().then((hints) => {
|
|
42
|
+
preloadEntries(mergeManifests(manifest, hints), [...plan.core, ...plan.plugins, ...plan.late], { isEditMode: true, hasSapJs });
|
|
43
|
+
return hints;
|
|
44
|
+
});
|
|
9
45
|
|
|
10
46
|
function domReady() {
|
|
11
47
|
return new Promise((resolve) => onDomReady(resolve));
|
|
12
48
|
}
|
|
13
49
|
|
|
50
|
+
// Where this document came from, when it is a copy of a starter template.
|
|
51
|
+
function templateSource() {
|
|
52
|
+
const meta = document.querySelector('meta[name="hyper-source"]');
|
|
53
|
+
return meta ? meta.getAttribute("content") : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
14
56
|
// `base` has been unused since 1.1.0 (the loader imports through MODULES) and
|
|
15
57
|
// stays anyway: /v1/clay.js and /v1/src/loader.js are cached independently, so
|
|
16
58
|
// for a while after a deploy a browser can pair either file with the other's
|
|
17
59
|
// previous release. A call shape that differs between them leaves the page with
|
|
18
|
-
// no clayjs at all.
|
|
19
|
-
|
|
60
|
+
// no clayjs at all. The optional arguments after the third come only from the chunked
|
|
61
|
+
// bootstrap: the manifest a reader downloads, the edit supplement an explicit
|
|
62
|
+
// ?editmode=true page fetched beside it, and the cached fetch of that supplement for
|
|
63
|
+
// every other resolved edit or draft. Their absence means no preloading.
|
|
64
|
+
export async function boot(base, params, readyResolve, manifest = null, supplement = null, loadEditHints = null) {
|
|
20
65
|
initTheme();
|
|
21
66
|
const draftMode = await MODULES["core/draft-mode.js"]();
|
|
22
67
|
draftMode.noteBootParams(params);
|
|
@@ -26,7 +71,11 @@ export async function boot(base, params, readyResolve) {
|
|
|
26
71
|
// would otherwise observe null
|
|
27
72
|
|
|
28
73
|
const editMode = await MODULES["core/is-edit-mode.js"]();
|
|
29
|
-
const { isEditMode, isOwner, siteVisibility } = editMode;
|
|
74
|
+
const { isEditMode, isDraftMode, isOwner, siteVisibility, canEdit } = editMode;
|
|
75
|
+
|
|
76
|
+
// A draft saved by its visitor never reaches the host, and hostMeta() answers
|
|
77
|
+
// bareHost() for it, so the request would fetch an answer nothing reads.
|
|
78
|
+
if (isEditMode && !isDraftMode) import("./core/host-permission.js").then((m) => m.checkHostPermission()).catch(() => {});
|
|
30
79
|
|
|
31
80
|
// richclay's vendor build detects edit mode via this legacy global; set it
|
|
32
81
|
// before any plugin import so its autoInit sees the right value. Overwrite
|
|
@@ -36,19 +85,67 @@ export async function boot(base, params, readyResolve) {
|
|
|
36
85
|
|
|
37
86
|
const regionPolicy = await MODULES["lib/region-policy.js"]();
|
|
38
87
|
|
|
39
|
-
const { isDraftMode } = editMode;
|
|
40
|
-
|
|
41
88
|
const hasSap = Boolean(document.querySelector("[sap]"));
|
|
89
|
+
// derive.js imports the sapjs core only when no sap.js script took the page over.
|
|
90
|
+
const hasSapJs = Boolean(document.querySelector('script[src*="sap.js"]'));
|
|
42
91
|
bootPhase.holdSettle = hasSap && isEditMode;
|
|
43
|
-
const plan = resolveModules(params, isEditMode, { isDraftMode, hasSap });
|
|
44
|
-
|
|
92
|
+
const plan = resolveModules(params, isEditMode, { isDraftMode, hasSap, canEdit, templateSource: templateSource() });
|
|
93
|
+
// Where edits land, resolved once: a visitor draft saves into its draft slot, the
|
|
94
|
+
// demo plugin into localStorage, and everything else to the host. setMode takes no
|
|
95
|
+
// destination, and V4 has no draft-to-host transition.
|
|
96
|
+
if (plan.plugins.includes("plugins/demo.js")) editMode.commitModeFacts({ destination: "demo" });
|
|
97
|
+
preloadEntries(mergeManifests(manifest, supplement), [...plan.core, ...plan.plugins], { isEditMode, isDraftMode, hasSapJs });
|
|
98
|
+
// A resolved initial edit or draft fetches the release's hint index, whether or not
|
|
99
|
+
// the URL announced it. It lands beside preparation and only ever adds modulepreloads:
|
|
100
|
+
// a failed fetch defers hints, never an import already mapped.
|
|
101
|
+
if ((isEditMode || isDraftMode) && !supplement && loadEditHints) {
|
|
102
|
+
loadEditHints()
|
|
103
|
+
.then((hints) => preloadEntries(mergeManifests(manifest, hints), [...plan.core, ...plan.plugins], { isEditMode, isDraftMode, hasSapJs }))
|
|
104
|
+
.catch(() => {});
|
|
105
|
+
}
|
|
106
|
+
// Not awaited by boot: an entry that overlaps the tail waits on this rather than
|
|
107
|
+
// import the same paths a second time.
|
|
108
|
+
let settleLate;
|
|
109
|
+
const lateDone = new Promise((resolve) => { settleLate = resolve; });
|
|
110
|
+
// The reader's transport choice comes from the plan just resolved, never from the
|
|
111
|
+
// URL: when generic sync loaded, it owns the saved lane and the reader follows
|
|
112
|
+
// the frames it applies rather than opening a second stream.
|
|
113
|
+
const readerOptions = { sync: plan.plugins.includes("sync/live-sync.js") };
|
|
45
114
|
|
|
46
115
|
for (const path of plan.core) {
|
|
47
116
|
loaded[path] = await MODULES[path](); // sequential: order is load-bearing
|
|
117
|
+
// The loader starts what it imports: a target that exports `install` gets it called
|
|
118
|
+
// here, once, in this order. A module another road reaches (a live switch, a second
|
|
119
|
+
// entry) then reuses that same installation instead of adding a second one.
|
|
120
|
+
loaded[path].install?.();
|
|
48
121
|
}
|
|
49
122
|
|
|
50
|
-
assembleCore(loaded, { isDraftMode,
|
|
51
|
-
|
|
123
|
+
assembleCore(loaded, { isDraftMode, isOwner, siteVisibility }, regionPolicy);
|
|
124
|
+
attachEditCore(loaded); // window.clay MUST be assembled before any plugin import
|
|
125
|
+
// A document that signs the live-switch contract hands its boot to the mode dispatcher.
|
|
126
|
+
// Nothing is fetched for it here: the decision and the controller behind it wait for the
|
|
127
|
+
// first switch, so this costs a reader with a declaration no more than one attribute.
|
|
128
|
+
if (document.documentElement.getAttribute("clay-mode-switch") === "live") {
|
|
129
|
+
const editing = resolveModules(params, true, { isDraftMode, hasSap, canEdit, templateSource: templateSource() });
|
|
130
|
+
loaded["core/edit-mode.js"].noteLiveSwitch({
|
|
131
|
+
edit: editing,
|
|
132
|
+
// The plan a view boot would have resolved, not the edit plan this boot resolved: an
|
|
133
|
+
// exit imports it and starts its reader, and an edit-only module belongs to the
|
|
134
|
+
// session being left.
|
|
135
|
+
view: resolveModules(params, false, { isDraftMode, hasSap, canEdit, templateSource: templateSource() }),
|
|
136
|
+
loaded,
|
|
137
|
+
modules: MODULES,
|
|
138
|
+
attach: attachPluginMember,
|
|
139
|
+
attachEditCore,
|
|
140
|
+
announce,
|
|
141
|
+
loadEditHints: loadEditHints ? editHints(loadEditHints, manifest, editing, hasSapJs) : null,
|
|
142
|
+
late: lateDone,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// An explicit initial edit drains the preboot participant queue and enables its
|
|
146
|
+
// leases before a plugin can take input. A reader returns at the first line.
|
|
147
|
+
await loaded["core/edit-mode.js"].activateInitialMode();
|
|
148
|
+
window.clay.plugins = Object.freeze([...plan.plugins, ...plan.late].map((path) => PLUGIN_NAMES[path]));
|
|
52
149
|
|
|
53
150
|
for (const path of plan.plugins) {
|
|
54
151
|
// A plugin that throws on evaluation degrades to absent. Unguarded, its
|
|
@@ -64,6 +161,7 @@ export async function boot(base, params, readyResolve) {
|
|
|
64
161
|
continue;
|
|
65
162
|
}
|
|
66
163
|
loaded[path] = mod;
|
|
164
|
+
if (path === "plugins/page-view.js") mod.start(readerOptions);
|
|
67
165
|
attachPluginMember(path, mod, loaded); // immediately, not after the loop: hypercms's ?cms=true
|
|
68
166
|
// auto-open runs as a microtask queued during ITS evaluation
|
|
69
167
|
// (before boot resumes) and reads clay.RichClay and clay.undo,
|
|
@@ -76,9 +174,50 @@ export async function boot(base, params, readyResolve) {
|
|
|
76
174
|
|
|
77
175
|
if (hasSap) mountDerivedEngine(loaded);
|
|
78
176
|
|
|
79
|
-
installViewModeNotice(
|
|
177
|
+
installViewModeNotice(editMode);
|
|
80
178
|
readyResolve(window.clay);
|
|
81
179
|
document.dispatchEvent(new CustomEvent("clay:ready", { detail: { clay: window.clay } }));
|
|
180
|
+
if (plan.late.length) loadLate(plan.late, editMode).then(settleLate, settleLate);
|
|
181
|
+
else settleLate();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// An edit-only plugin belongs to the edit session that resolved it. Once the page has
|
|
185
|
+
// left edit, importing it would start an editor in a view, so the tail drops it instead:
|
|
186
|
+
// the module is not fetched, not attached and not announced. Dropping is not a reload —
|
|
187
|
+
// nothing is re-imported with a cache-busting URL, and a later entry asks the module
|
|
188
|
+
// cache for the same single instance.
|
|
189
|
+
//
|
|
190
|
+
// A transition in flight counts as left: until it commits, `isEditMode` still reads the
|
|
191
|
+
// mode being left, and a module that evaluated then would start an unadopted session. The
|
|
192
|
+
// modules ask the same question themselves (`startsAtImport`), so an import already in
|
|
193
|
+
// flight when the exit began starts nothing either.
|
|
194
|
+
const stale = (path, editMode) =>
|
|
195
|
+
PLUGIN_PATHS[PLUGIN_NAMES[path]]?.editOnly && !editMode.startsAtImport();
|
|
196
|
+
|
|
197
|
+
// Plugins no first paint or keystroke waits for. They load after the editor takes input
|
|
198
|
+
// (right away on a page without one), one at a time and in plan order, and a failure
|
|
199
|
+
// degrades to absent as at boot.
|
|
200
|
+
async function loadLate(paths, editMode) {
|
|
201
|
+
try {
|
|
202
|
+
await window.clay.page?.ready;
|
|
203
|
+
} catch {}
|
|
204
|
+
for (const path of paths) {
|
|
205
|
+
if (stale(path, editMode)) continue;
|
|
206
|
+
try {
|
|
207
|
+
const mod = await MODULES[path]();
|
|
208
|
+
// The session can end while the fetch is in flight, so ask again before the module
|
|
209
|
+
// is given a page to wire itself into.
|
|
210
|
+
if (stale(path, editMode)) continue;
|
|
211
|
+
loaded[path] = mod;
|
|
212
|
+
attachPluginMember(path, mod, loaded);
|
|
213
|
+
await mod.ready;
|
|
214
|
+
if (stale(path, editMode)) continue;
|
|
215
|
+
announce(path);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
console.error(`clayjs: plugin "${path}" failed to load, continuing without it:`, err);
|
|
218
|
+
document.dispatchEvent(new CustomEvent("clay:plugin-failed", { detail: { plugin: PLUGIN_NAMES[path] } }));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
82
221
|
}
|
|
83
222
|
|
|
84
223
|
// The explicit mount phase for a derive engine (sapjs or its core). It runs after
|
|
@@ -104,7 +243,7 @@ export function mountDerivedEngine(loaded) {
|
|
|
104
243
|
if (save && fileSignature != null) save.schedulePaintHeal(fileSignature);
|
|
105
244
|
}
|
|
106
245
|
|
|
107
|
-
function assembleCore(loaded, { isDraftMode,
|
|
246
|
+
function assembleCore(loaded, { isDraftMode, isOwner, siteVisibility }, regionPolicy) {
|
|
108
247
|
const clay = window.clay;
|
|
109
248
|
|
|
110
249
|
const mutation = loaded["lib/mutation.js"];
|
|
@@ -112,63 +251,106 @@ function assembleCore(loaded, { isDraftMode, isEditMode, isOwner, siteVisibility
|
|
|
112
251
|
|
|
113
252
|
Object.assign(clay, {
|
|
114
253
|
toggleEditMode: editModeMod.toggleEditMode,
|
|
254
|
+
setMode: editModeMod.setMode,
|
|
255
|
+
modeLifecycle: editModeMod.modeLifecycle,
|
|
115
256
|
isDraftMode,
|
|
116
|
-
isEditMode,
|
|
117
257
|
isOwner,
|
|
118
258
|
siteVisibility,
|
|
119
259
|
Mutation: mutation.default,
|
|
120
260
|
region: regionPolicy.windowRegionShape,
|
|
121
261
|
});
|
|
122
262
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
263
|
+
// Getters over the live mode facts, never copies: the lifecycle commits a new mode
|
|
264
|
+
// and these read it back, so `clay.mode` stays a string that cannot go stale.
|
|
265
|
+
for (const key of ["mode", "isEditMode", "canEdit"]) {
|
|
266
|
+
Object.defineProperty(clay, key, { get: () => editModeMod[key], enumerable: true, configurable: true });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
clay.assets = assets;
|
|
270
|
+
|
|
127
271
|
const pageData = loaded["core/page-data.js"];
|
|
128
272
|
|
|
129
273
|
clay.readData = pageData.readData;
|
|
130
274
|
clay.writeData = pageData.writeData;
|
|
131
275
|
clay.ephemeral = ephemeral;
|
|
276
|
+
clay.storage = storage;
|
|
277
|
+
// About the document rather than the page, and asked for on use: a reader never
|
|
278
|
+
// downloads the discovery client for it.
|
|
279
|
+
clay.host = {
|
|
280
|
+
get versions() {
|
|
281
|
+
return import("./core/host-meta.js").then((m) => m.hostVersions());
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
clay.trash = trash;
|
|
132
285
|
clay.derived = derived;
|
|
133
286
|
clay.effect = effect;
|
|
134
287
|
clay.style = style;
|
|
288
|
+
clay.morph = (oldEl, newEl, options) => mergeEngine().then(({ morph }) => morph(oldEl, newEl, options));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// The edit half of the assembly: boot runs it right after the always half, and a live entry
|
|
292
|
+
// runs it once its edit core has landed. It adds what those modules offer and replaces
|
|
293
|
+
// nothing already there, so a member a reader attached first survives.
|
|
294
|
+
function attachEditCore(loaded) {
|
|
295
|
+
const clay = window.clay;
|
|
135
296
|
|
|
297
|
+
const save = loaded["core/save.js"];
|
|
136
298
|
if (save) {
|
|
137
299
|
const saveFn = save.savePage || save.default;
|
|
138
300
|
saveFn.force = save.savePageForce;
|
|
139
301
|
saveFn.overwrite = save.saveOverwritingConflict;
|
|
140
302
|
saveFn.flush = save.flushSave;
|
|
141
|
-
|
|
303
|
+
saveFn.soon = save.savePageSoon;
|
|
304
|
+
// What the host says it will store, and a measurement of candidate bytes. Both
|
|
305
|
+
// are asked for on use: a reader has neither, and a page that never asks pays
|
|
306
|
+
// nothing.
|
|
307
|
+
saveFn.limits = save.saveLimits;
|
|
308
|
+
saveFn.inspect = save.inspectSaveSize;
|
|
309
|
+
// An upgrade-capable reader's temporary save object hands its `replace` to the function.
|
|
310
|
+
if (clay.save !== saveFn && typeof clay.save?.replace === "function") saveFn.replace = clay.save.replace;
|
|
311
|
+
if (clay.save !== saveFn) clay.save = saveFn;
|
|
142
312
|
}
|
|
143
313
|
// The close warning's own predicate, reachable without a capture. It falls back to
|
|
144
314
|
// false rather than to absent: the module is edit-only, and a document that cannot
|
|
145
315
|
// save has nothing unsaved. Older clayjs has no such member, and that is the answer
|
|
146
316
|
// a host feature-detects.
|
|
147
|
-
clay.hasUnsavedChanges =
|
|
317
|
+
clay.hasUnsavedChanges =
|
|
318
|
+
loaded["core/unsaved-warning.js"]?.hasUnsavedChanges ?? clay.hasUnsavedChanges ?? (() => false);
|
|
319
|
+
const snapshot = loaded["core/snapshot.js"];
|
|
148
320
|
if (snapshot) {
|
|
149
|
-
clay.getHTML = snapshot.getPageContents;
|
|
150
|
-
clay.addDocumentTransform = snapshot.addDocumentTransform;
|
|
151
|
-
clay.onSnapshot = snapshot.onSnapshot;
|
|
321
|
+
if (!clay.getHTML) clay.getHTML = snapshot.getPageContents;
|
|
322
|
+
if (!clay.addDocumentTransform) clay.addDocumentTransform = snapshot.addDocumentTransform;
|
|
323
|
+
if (!clay.onSnapshot) clay.onSnapshot = snapshot.onSnapshot;
|
|
152
324
|
}
|
|
153
|
-
|
|
325
|
+
const cacheBustMod = loaded["lib/cache-bust.js"];
|
|
326
|
+
if (cacheBustMod && !clay.cacheBust) {
|
|
154
327
|
clay.cacheBust = cacheBustMod.default;
|
|
155
328
|
}
|
|
156
|
-
|
|
329
|
+
// Edit mode only, and absent entirely in view mode: a reader has nothing to
|
|
330
|
+
// recover and must never open the database. The facade is here; the store behind
|
|
331
|
+
// it arrives when the page's first adapter registers a channel.
|
|
332
|
+
const recoveryMod = loaded["core/recovery.js"];
|
|
333
|
+
if (recoveryMod && !clay.recovery) clay.recovery = recoveryMod.recovery;
|
|
157
334
|
}
|
|
158
335
|
|
|
159
|
-
function attachPluginMember(path, mod, loaded) {
|
|
336
|
+
function attachPluginMember(path, mod, namespaces = loaded) {
|
|
160
337
|
const clay = window.clay;
|
|
161
338
|
|
|
162
339
|
if (path === "plugins/undo.js") {
|
|
163
340
|
clay.undo = mod.undo || mod.default;
|
|
341
|
+
} else if (path === "plugins/unsent.js") {
|
|
342
|
+
clay.unsent = mod.unsent;
|
|
164
343
|
} else if (path === "sync/live-sync.js") {
|
|
165
344
|
clay.morph = mod.morph;
|
|
166
345
|
clay.conflicts = mod.conflicts;
|
|
167
|
-
|
|
346
|
+
namespaces["core/page-data.js"].setPageDataLiveSync(mod.liveSync);
|
|
168
347
|
} else if (path === "plugins/live.js") {
|
|
169
348
|
clay.live = mod.live;
|
|
170
349
|
} else if (path === "plugins/page.js") {
|
|
171
350
|
clay.page = mod.page;
|
|
351
|
+
} else if (path === "plugins/upgrade.js") {
|
|
352
|
+
clay.save = clay.save || {};
|
|
353
|
+
clay.save.replace = mod.replace;
|
|
172
354
|
} else if (path === "plugins/cms.js") {
|
|
173
355
|
clay.cms = mod.cms || mod.default;
|
|
174
356
|
} else if (path === "plugins/upload.js") {
|
|
@@ -189,17 +371,23 @@ function attachPluginMember(path, mod, loaded) {
|
|
|
189
371
|
clay.RichClay = mod.RichClay || mod.default;
|
|
190
372
|
} else if (path === "plugins/quickcrop.js") {
|
|
191
373
|
clay.quickcrop = mod.quickcrop || mod.default;
|
|
374
|
+
} else if (path === "plugins/sortable.js") {
|
|
375
|
+
// With the module, not with its vendor: a move answers before a drag can start.
|
|
376
|
+
clay.sortable = mod.sortable;
|
|
192
377
|
} else if (path === "plugins/source.js") {
|
|
193
378
|
clay.source = mod.source || mod.default;
|
|
194
|
-
} else if (path === "plugins/visitor-drafts.js") {
|
|
379
|
+
} else if (path === "plugins/visitor-drafts.js" || path === "plugins/visitor-drafts-owner.js") {
|
|
195
380
|
clay.visitorDrafts = mod.visitorDrafts;
|
|
381
|
+
} else if (path === "plugins/bind-text.js") {
|
|
382
|
+
clay.bindText = mod.bindText;
|
|
196
383
|
}
|
|
197
384
|
}
|
|
198
385
|
|
|
199
|
-
function installViewModeNotice(
|
|
200
|
-
if (isEditMode) return;
|
|
386
|
+
function installViewModeNotice(editMode) {
|
|
387
|
+
if (editMode.isEditMode) return;
|
|
201
388
|
document.addEventListener("click", (event) => {
|
|
202
389
|
if (!event.target.closest?.("[trigger-save]")) return;
|
|
390
|
+
if (editMode.isEditMode) return;
|
|
203
391
|
console.info("clayjs: you're not the owner of this page; changes stay local");
|
|
204
392
|
document.dispatchEvent(new CustomEvent("clay:view-save-attempt"));
|
|
205
393
|
});
|
package/src/page/adopt-boot.js
CHANGED
|
@@ -10,9 +10,27 @@ import { mergePageModels } from "./merge.js";
|
|
|
10
10
|
import { blockMap } from "./model.js";
|
|
11
11
|
import { parseModel } from "./projection.js";
|
|
12
12
|
import { ARTICLE, pageTags } from "./save-projection.js";
|
|
13
|
-
import {
|
|
13
|
+
import { parseJsonRelaxed } from "../vendor/hyper-morph-json.vendor.js";
|
|
14
|
+
import { loadedEngine, mergeEngine } from "../sync/merge-engine.js";
|
|
14
15
|
|
|
15
|
-
const parseTag = (text) => { try { return
|
|
16
|
+
const parseTag = (text) => { try { return parseJsonRelaxed(text); } catch { return null; } };
|
|
17
|
+
|
|
18
|
+
// What one adoption attempt did, so a caller can tell a landed adoption from a
|
|
19
|
+
// stopped, refused or failed one. `ok` is true only when the store holds the
|
|
20
|
+
// accepted state afterwards, whether that meant splicing the file in or finding
|
|
21
|
+
// nothing to splice.
|
|
22
|
+
const UNCHANGED = { ok: true, outcome: "unchanged" };
|
|
23
|
+
const STOPPED = { ok: false, outcome: "disposed", reason: "adoption-disposed" };
|
|
24
|
+
const REFUSED = { ok: false, outcome: "refused", reason: "adoption-refused" };
|
|
25
|
+
const TIMED_OUT = { ok: false, outcome: "refused", reason: "adoption-timeout" };
|
|
26
|
+
const FAILED = { ok: false, outcome: "error", reason: "adoption-error" };
|
|
27
|
+
// Another tab holds the room's writer seat: the adoption is that tab's to land,
|
|
28
|
+
// and it reaches this one through the room. Not a refusal, and not a success yet.
|
|
29
|
+
const AWAITED = { ok: true, outcome: "writer-adopts" };
|
|
30
|
+
const landed = (counts) => ({ ok: true, outcome: "adopted", counts });
|
|
31
|
+
|
|
32
|
+
// The bound on adoption, and on waiting for the elected writer's adoption.
|
|
33
|
+
export const ADOPTION_TIMEOUT_MS = 10_000;
|
|
16
34
|
|
|
17
35
|
export function readFile(root) {
|
|
18
36
|
const article = root.querySelector(ARTICLE);
|
|
@@ -40,7 +58,7 @@ export function installAdoption({
|
|
|
40
58
|
adopted = () => {},
|
|
41
59
|
hold = suspendAutosave,
|
|
42
60
|
release = resumeAutosave,
|
|
43
|
-
holdMs =
|
|
61
|
+
holdMs = ADOPTION_TIMEOUT_MS,
|
|
44
62
|
doc = document,
|
|
45
63
|
}) {
|
|
46
64
|
let last = source ? source.article.innerHTML : null;
|
|
@@ -81,6 +99,7 @@ export function installAdoption({
|
|
|
81
99
|
|
|
82
100
|
// The editor merged three-way with what the file changed since base.
|
|
83
101
|
function land(blocks, current, baseItems, incoming) {
|
|
102
|
+
const { HyperMorph } = loadedEngine();
|
|
84
103
|
const merged = mergePageModels(baseItems, current, incoming, { doc, merge3: HyperMorph.merge3, mergeJson: HyperMorph.mergeJson });
|
|
85
104
|
const lost = merged.conflicts.filter((k) => k.kind === "text" || k.kind === "field");
|
|
86
105
|
const copy = lost.length ? snapshot() : null;
|
|
@@ -101,16 +120,25 @@ export function installAdoption({
|
|
|
101
120
|
if (mergeTagless && !file.tags.length) return adoptTagless(file);
|
|
102
121
|
const found = classifyFile(file.article, file.tags);
|
|
103
122
|
if (found.kind === "unbased") console.warn("page: the article and its data changed together; kept the data");
|
|
104
|
-
if (found.kind !== "adopt"
|
|
123
|
+
if (found.kind !== "adopt") return UNCHANGED;
|
|
124
|
+
// Only the room's writer adopts, so another tab's write is this tab's adoption
|
|
125
|
+
// too: it arrives through the room, and the caller waits for it there.
|
|
126
|
+
if (!isAdopter()) return AWAITED;
|
|
105
127
|
hold();
|
|
106
128
|
try {
|
|
129
|
+
await mergeEngine();
|
|
130
|
+
if (disposed) return STOPPED;
|
|
131
|
+
if (!isAdopter()) return REFUSED;
|
|
107
132
|
const waited = await waitForEditor();
|
|
108
133
|
if (waited === "timeout") console.warn("page: gave up waiting for the editor; the hand edit was not adopted");
|
|
109
|
-
if (waited
|
|
134
|
+
if (waited === "disposed" || disposed) return STOPPED;
|
|
135
|
+
if (waited !== "ready") return TIMED_OUT;
|
|
136
|
+
if (!isAdopter()) return REFUSED;
|
|
110
137
|
const blocks = blockMap(view.state.doc);
|
|
111
138
|
const counts = land(blocks, readBack(blocks, doc), found.baseItems, found.parsed);
|
|
112
|
-
if (counts)
|
|
113
|
-
|
|
139
|
+
if (!counts) return UNCHANGED;
|
|
140
|
+
adopted(found.A);
|
|
141
|
+
return landed(counts);
|
|
114
142
|
} finally {
|
|
115
143
|
release();
|
|
116
144
|
}
|
|
@@ -121,39 +149,60 @@ export function installAdoption({
|
|
|
121
149
|
const list = (rev && seen.get(rev)) || [];
|
|
122
150
|
const base = list.at(-1) ?? disk;
|
|
123
151
|
// An untouched older copy leaves the page as it is.
|
|
124
|
-
if (list.slice(0, -1).some((article) => article.innerHTML === file.article.innerHTML)) return
|
|
152
|
+
if (list.slice(0, -1).some((article) => article.innerHTML === file.article.innerHTML)) return UNCHANGED;
|
|
125
153
|
record(file.article);
|
|
126
154
|
disk = file.article;
|
|
127
|
-
if (!base) return
|
|
155
|
+
if (!base) return UNCHANGED;
|
|
156
|
+
// The latest copy again, attributes and all: merging it into the page changes nothing.
|
|
157
|
+
if (base.outerHTML === file.article.outerHTML) return UNCHANGED;
|
|
128
158
|
// Only the room's elected writer merges a frame, so it lands once however
|
|
129
159
|
// many tabs see it; and it lands even when the editor never settles, since
|
|
130
160
|
// the next save would otherwise write over the edit.
|
|
131
161
|
hold();
|
|
132
162
|
try {
|
|
163
|
+
await mergeEngine();
|
|
164
|
+
if (disposed) return STOPPED;
|
|
165
|
+
if (!isAdopter()) return REFUSED;
|
|
133
166
|
await writerReady();
|
|
134
|
-
if (disposed
|
|
167
|
+
if (disposed) return STOPPED;
|
|
168
|
+
if (!isAdopter()) return REFUSED;
|
|
135
169
|
const waited = await waitForEditor();
|
|
136
|
-
if (waited === "disposed" || disposed
|
|
170
|
+
if (waited === "disposed" || disposed) return STOPPED;
|
|
171
|
+
if (!isAdopter()) return REFUSED;
|
|
137
172
|
const blocks = blockMap(view.state.doc);
|
|
138
173
|
const current = readBack(blocks, doc);
|
|
139
174
|
const known = new Map(current.map((item) => [item.id, item]));
|
|
140
175
|
const parse = (article) => parseModel(article, { seed: article.getAttribute("data-rev") ?? "", known });
|
|
141
|
-
|
|
176
|
+
const counts = land(blocks, current, parse(base), parse(file.article));
|
|
177
|
+
return counts ? landed(counts) : UNCHANGED;
|
|
142
178
|
} finally {
|
|
143
179
|
release();
|
|
144
180
|
}
|
|
145
181
|
}
|
|
146
182
|
|
|
147
|
-
|
|
183
|
+
// A failed attempt is an outcome, never a resolved success: the coordinator
|
|
184
|
+
// decides on `ok`, and the outside world keeps its events and quiet queue.
|
|
185
|
+
async function attempt(file) {
|
|
186
|
+
try {
|
|
187
|
+
return await adopt(file);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error("page: adoption failed", error);
|
|
190
|
+
return FAILED;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let bootOutcome = null;
|
|
195
|
+
function run(file, boot = false) {
|
|
148
196
|
queue = queue
|
|
149
|
-
.then(() =>
|
|
150
|
-
.then((
|
|
151
|
-
if (
|
|
197
|
+
.then(() => attempt(file))
|
|
198
|
+
.then((outcome) => {
|
|
199
|
+
if (boot) bootOutcome = outcome;
|
|
200
|
+
if (!outcome.ok || outcome.outcome !== "adopted") return outcome;
|
|
152
201
|
requestCheckpoint();
|
|
153
|
-
notice(counts);
|
|
154
|
-
doc.dispatchEvent(new CustomEvent("clay:page-adopted", { detail: counts }));
|
|
155
|
-
|
|
156
|
-
|
|
202
|
+
notice(outcome.counts);
|
|
203
|
+
doc.dispatchEvent(new CustomEvent("clay:page-adopted", { detail: outcome.counts }));
|
|
204
|
+
return outcome;
|
|
205
|
+
});
|
|
157
206
|
return queue;
|
|
158
207
|
}
|
|
159
208
|
|
|
@@ -167,10 +216,15 @@ export function installAdoption({
|
|
|
167
216
|
}
|
|
168
217
|
|
|
169
218
|
doc.addEventListener("clay:sync-applied", onFrame);
|
|
170
|
-
if (source) run(source);
|
|
219
|
+
if (source) run(source, true);
|
|
171
220
|
|
|
172
221
|
return {
|
|
173
|
-
|
|
222
|
+
// Resolves once everything queued so far has been attempted, with the boot
|
|
223
|
+
// file's own outcome: landed, unchanged, stopped, refused or failed.
|
|
224
|
+
idle: async () => {
|
|
225
|
+
await queue;
|
|
226
|
+
return bootOutcome;
|
|
227
|
+
},
|
|
174
228
|
// A save of this tab landed: the file holds what it sent, so that article is
|
|
175
229
|
// the one an outside edit made on the saved file merges against.
|
|
176
230
|
saved(html) {
|
package/src/page/adopt.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
// Cmd+Z takes the whole adoption back.
|
|
10
10
|
import { model as pmModel, history } from "../vendor/prosemirror.vendor.js";
|
|
11
11
|
import { blockMap, nodeFromBlock, repairSiblings } from "./model.js";
|
|
12
|
-
import { project, canon, revOf, parseModel, renderModel, fragmentCleaner } from "./projection.js";
|
|
12
|
+
import { DEFAULTS, project, canon, revOf, parseModel, renderModel, fragmentCleaner, unitsOf } from "./projection.js";
|
|
13
|
+
import { unflatten } from "./inline.js";
|
|
13
14
|
import { blocksFromPageValues } from "./ybind/decode.js";
|
|
14
15
|
|
|
15
16
|
export const ADOPT = "page-adopt";
|
|
@@ -56,7 +57,7 @@ export function blockFromItem(item, cur) {
|
|
|
56
57
|
const { id, t, ...fields } = item;
|
|
57
58
|
const out = { t, pos: cur?.pos ?? null };
|
|
58
59
|
Object.assign(out, fields);
|
|
59
|
-
if (t === "table" && !fields.rows.length) fields.rows = [{ id: `r-${id}`, cells: {} }];
|
|
60
|
+
if (t === "table" && !fields.rows.length && fields.cols.length) fields.rows = [{ id: `r-${id}`, cells: {} }];
|
|
60
61
|
if (t === "table") {
|
|
61
62
|
out.cols = keyed(fields.cols, cur?.cols, "c", ({ id: _i, ...c }) => c);
|
|
62
63
|
out.rows = keyed(fields.rows, cur?.rows, "r", ({ id: _i, cells, pos }) => ({ pos, cells }));
|
|
@@ -74,6 +75,23 @@ export function blockFromItem(item, cur) {
|
|
|
74
75
|
|
|
75
76
|
// ---- the transaction ---------------------------------------------------------
|
|
76
77
|
|
|
78
|
+
// A block type this runtime has no schema for keeps its whole record in the
|
|
79
|
+
// inert node, and its text for mapping.
|
|
80
|
+
const opaqueNode = (schema, id, block) => {
|
|
81
|
+
const { t, pos, c, m, ...raw } = block;
|
|
82
|
+
const content = typeof c === "string" && c ? unflatten(schema, c, unitsOf(m, c.length), schema.nodes.opaque_block) : null;
|
|
83
|
+
return schema.nodes.opaque_block.create({ id, pos, raw: { t, ...raw } }, content);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// The editor’s blocks as the article and the record state them: an opaque node
|
|
87
|
+
// stands for the record it carries, never for a block type of its own.
|
|
88
|
+
const recordsOf = (blocks) => Object.fromEntries(Object.entries(blocks).map(([id, b]) => {
|
|
89
|
+
if (b.t !== "opaque_block") return [id, b];
|
|
90
|
+
const { t: _t, raw, ...rest } = b;
|
|
91
|
+
const { t, ...fields } = raw ?? {};
|
|
92
|
+
return [id, { t: t ?? "paragraph", pos: b.pos, ...fields, ...rest }];
|
|
93
|
+
}));
|
|
94
|
+
|
|
77
95
|
function find(doc, id) {
|
|
78
96
|
let found = null;
|
|
79
97
|
doc.forEach((node, pos) => { if (!found && node.attrs.id === id) found = { node, pos }; });
|
|
@@ -119,7 +137,7 @@ export function adoptionTransaction(state, plan) {
|
|
|
119
137
|
if (!plan) return null;
|
|
120
138
|
const schema = state.schema;
|
|
121
139
|
const doc = state.doc;
|
|
122
|
-
const current = blockMap(doc);
|
|
140
|
+
const current = recordsOf(blockMap(doc));
|
|
123
141
|
const tr = state.tr;
|
|
124
142
|
const projected = new Map(project(current).map((item) => [item.id, item]));
|
|
125
143
|
const displaced = {};
|
|
@@ -127,7 +145,10 @@ export function adoptionTransaction(state, plan) {
|
|
|
127
145
|
const was = plan.base.get(id), now = projected.get(id);
|
|
128
146
|
if (was && now && !same(body(was), body(now))) displaced[id] = current[id];
|
|
129
147
|
}
|
|
130
|
-
const nodeOf = (id, item) =>
|
|
148
|
+
const nodeOf = (id, item) => {
|
|
149
|
+
const block = blockFromItem(item, current[id]);
|
|
150
|
+
return Object.hasOwn(DEFAULTS, item.t) ? nodeFromBlock(schema, id, block) : opaqueNode(schema, id, block);
|
|
151
|
+
};
|
|
131
152
|
|
|
132
153
|
// Adopting into a page that had no blocks: the editor's empty placeholder line
|
|
133
154
|
// goes, after the article's blocks are in (an empty doc would get a new one).
|