@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/page/editor.js
CHANGED
|
@@ -6,30 +6,30 @@
|
|
|
6
6
|
// `page` cascade layer, so a theme is one plain stylesheet that sets tokens on
|
|
7
7
|
// .page-shell (and adds scoped rules where it needs to); unlayered theme rules
|
|
8
8
|
// always win. Contract: plans/.../default-page/v1/look/THEME-CONTRACT.md
|
|
9
|
-
import { state, view as pmView, keymap, commands, history, gapcursor, inputrules } from "../vendor/prosemirror.vendor.js";
|
|
9
|
+
import { state, view as pmView, model, keymap, commands, history, gapcursor, inputrules } from "../vendor/prosemirror.vendor.js";
|
|
10
10
|
import { schema } from "./schema.js";
|
|
11
11
|
import { identityPlugin, docFromBlocks, blockMap, keyBetween, newId } from "./model.js";
|
|
12
12
|
import { blockKeys, structurePlugin, collapsePlugin, pastePlugin, BlockSelection, STEP } from "./blocks.js";
|
|
13
|
-
import { chromePlugin } from "./chrome.js";
|
|
14
|
-
import { announcementPlugin } from "./announce.js";
|
|
13
|
+
import { chromePlugin, slashRangePlugin } from "./chrome.js";
|
|
14
|
+
import { announcementPlugin, announce } from "./announce.js";
|
|
15
15
|
import { linkPlugin } from "./link.js";
|
|
16
|
-
import { blockSelectionPlugin } from "./blocksel.js";
|
|
17
|
-
import { markdownPlugin, markdownKeys, backspaceToParagraph } from "./markdown.js";
|
|
16
|
+
import { blockSelectionPlugin, asStep } from "./blocksel.js";
|
|
17
|
+
import { markdownPlugin, markdownKeys, backspaceToParagraph, composing } from "./markdown.js";
|
|
18
|
+
import { markable } from "./format-native.js";
|
|
18
19
|
import { placeholderPlugin } from "./placeholder.js";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
20
|
+
import { titlePlugin } from "./document-title-runtime.js";
|
|
21
|
+
import { mediaPlugin } from "./media.js";
|
|
22
|
+
import { highlightSlot } from "./highlight-slot.js";
|
|
23
|
+
import { tableKeys, tablePlugin } from "./table.js";
|
|
22
24
|
import { tableSelectPlugin } from "./table-select.js";
|
|
23
|
-
import {
|
|
24
|
-
import { BoardView } from "./board-view.js";
|
|
25
|
-
import { boardPlugin, cardPanel } from "./board-panel.js";
|
|
26
|
-
import { ToolView } from "./tool-view.js";
|
|
27
|
-
import { createToolHost } from "./tools.js";
|
|
25
|
+
import { viewFeatures, viewHookPlugin } from "./view-hooks.js";
|
|
28
26
|
import { checklistPlugin, checklistKeys } from "./checklist.js";
|
|
29
27
|
import { csvImportPlugin } from "./csv-import.js";
|
|
30
|
-
import { TABLE_CSS } from "./table-style.js";
|
|
31
|
-
import { BOARD_CSS } from "./board-style.js";
|
|
32
28
|
import { styleWithLegacyId } from "../lib/style.js";
|
|
29
|
+
import { historyIntentPlugin } from "./history-intent.js";
|
|
30
|
+
import { createFeatureLoader } from "./feature-loader.js";
|
|
31
|
+
import { COMMAND_FEATURES, FEATURES, featureForType } from "./features/registry.js";
|
|
32
|
+
import { toolViews } from "./tool-views.js";
|
|
33
33
|
|
|
34
34
|
const STYLE_ID = "page-editor-style";
|
|
35
35
|
const INDENTS = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
@@ -39,6 +39,11 @@ const LIGHT = `
|
|
|
39
39
|
--page-rule: rgba(35, 34, 31, 0.10); --page-accent: #2F5E9E; --page-link: #164583; --page-selection: rgba(88, 120, 170, 0.20);
|
|
40
40
|
--page-check-border: rgba(35, 34, 31, 0.35); --page-check-fill: #23221F; --page-check-ink: #F7F6F2;
|
|
41
41
|
--page-callout-bg: rgba(35, 34, 31, 0.04); --page-code-bg: rgba(35, 34, 31, 0.05);
|
|
42
|
+
--page-color-gray: #5B5953; --page-color-brown: #8A5A1E; --page-color-orange: #B4531A; --page-color-yellow: #7A6210; --page-color-green: #3E6A28;
|
|
43
|
+
--page-color-blue: #1F5A8A; --page-color-purple: #5E4596; --page-color-pink: #A23E6E; --page-color-red: #9E3B2F;
|
|
44
|
+
--page-mark-bg-gray: rgba(35, 34, 31, 0.12); --page-mark-bg-brown: rgba(138, 90, 30, 0.20); --page-mark-bg-orange: rgba(180, 83, 26, 0.20);
|
|
45
|
+
--page-mark-bg-yellow: rgba(122, 98, 16, 0.22); --page-mark-bg-green: rgba(62, 106, 40, 0.20); --page-mark-bg-blue: rgba(31, 90, 138, 0.18);
|
|
46
|
+
--page-mark-bg-purple: rgba(94, 69, 150, 0.18); --page-mark-bg-pink: rgba(162, 62, 110, 0.18); --page-mark-bg-red: rgba(158, 59, 47, 0.18);
|
|
42
47
|
--page-code-token-keyword: #8A3B6E; --page-code-token-constant: #1F5A8A; --page-code-token-string: #3E6A28; --page-code-token-string-expression: #3E6A28; --page-code-token-comment: #8B877E; --page-code-token-function: #5E4596; --page-code-token-parameter: #8A5A1E; --page-code-token-punctuation: var(--page-ink-soft); --page-code-token-link: var(--page-link);
|
|
43
48
|
--page-chrome-ink-rest: rgba(35, 34, 31, 0.20); --page-chrome-ink-moved: rgba(35, 34, 31, 0.34);
|
|
44
49
|
--page-chrome-bg-rest: rgba(35, 34, 31, 0.02); --page-chrome-bg-moved: rgba(35, 34, 31, 0.04);
|
|
@@ -53,6 +58,11 @@ const DARK = `
|
|
|
53
58
|
--page-rule: rgba(231, 228, 220, 0.10); --page-accent: #8DB0E6; --page-link: #8DB0E6; --page-selection: rgba(120, 155, 210, 0.28);
|
|
54
59
|
--page-check-border: rgba(231, 228, 220, 0.35); --page-check-fill: #E7E4DC; --page-check-ink: #1A1A18;
|
|
55
60
|
--page-callout-bg: rgba(231, 228, 220, 0.05); --page-code-bg: rgba(231, 228, 220, 0.06);
|
|
61
|
+
--page-color-gray: #A3A098; --page-color-brown: #E0B47A; --page-color-orange: #E8A177; --page-color-yellow: #DEC97A; --page-color-green: #A9C98B;
|
|
62
|
+
--page-color-blue: #8DB0E6; --page-color-purple: #BBA6E6; --page-color-pink: #E3A3C4; --page-color-red: #E08A7A;
|
|
63
|
+
--page-mark-bg-gray: rgba(231, 228, 220, 0.14); --page-mark-bg-brown: rgba(224, 180, 122, 0.22); --page-mark-bg-orange: rgba(232, 161, 119, 0.22);
|
|
64
|
+
--page-mark-bg-yellow: rgba(222, 201, 122, 0.24); --page-mark-bg-green: rgba(169, 201, 139, 0.22); --page-mark-bg-blue: rgba(141, 176, 230, 0.22);
|
|
65
|
+
--page-mark-bg-purple: rgba(187, 166, 230, 0.22); --page-mark-bg-pink: rgba(227, 163, 196, 0.22); --page-mark-bg-red: rgba(224, 138, 122, 0.22);
|
|
56
66
|
--page-code-token-keyword: #D9A0C8; --page-code-token-constant: #8DB0E6; --page-code-token-string: #A9C98B; --page-code-token-string-expression: #A9C98B; --page-code-token-comment: #75726B; --page-code-token-function: #BBA6E6; --page-code-token-parameter: #E0B47A; --page-code-token-punctuation: var(--page-ink-soft); --page-code-token-link: var(--page-link);
|
|
57
67
|
--page-chrome-ink-rest: rgba(231, 228, 220, 0.18); --page-chrome-ink-moved: rgba(231, 228, 220, 0.30);
|
|
58
68
|
--page-chrome-bg-rest: rgba(231, 228, 220, 0.02); --page-chrome-bg-moved: rgba(231, 228, 220, 0.04);
|
|
@@ -100,6 +110,9 @@ export const PAGE_EDITOR_CSS = `
|
|
|
100
110
|
.page-editor pre:hover .page-code-lang, .page-editor pre:focus-within .page-code-lang, .page-code-lang:focus-visible, .page-editor pre[data-lang] .page-code-lang { opacity: 1; }
|
|
101
111
|
.page-code-lang:hover, .page-code-lang:focus-visible { background: var(--page-pop-hover); color: var(--page-ink-soft); }
|
|
102
112
|
:where(article[data-page], .page-editor) .page-callout { background: var(--page-callout-bg); border: 1px solid var(--page-callout-line); border-radius: 8px; padding: 10px 14px; }
|
|
113
|
+
/* The four floor-1 formats. A quote is set apart by its own inset and ink, with
|
|
114
|
+
no left-border accent; the palette's keys are fixed tokens, in both themes. */
|
|
115
|
+
:where(article[data-page], .page-editor) blockquote { padding-inline-start: var(--page-indent); color: var(--page-ink-soft); }
|
|
103
116
|
:where(article[data-page], .page-editor) hr { border: 0; border-top: 1px solid var(--page-rule); margin-block: 1.2em; }
|
|
104
117
|
:where(article[data-page], .page-editor) .page-hidden { display: none; }
|
|
105
118
|
:where(article[data-page], .page-editor) .page-li { position: relative; --li-indent: 0; padding-inline-start: calc((var(--li-indent) + 1) * var(--page-indent)); }
|
|
@@ -126,7 +139,10 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
|
|
|
126
139
|
.page-tool { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 6px; border: 0; border-radius: 5px; background: none; color: inherit; font: 500 13px/1 var(--page-font); cursor: pointer; }
|
|
127
140
|
.page-tool:hover, .page-tool[aria-pressed="true"] { background: var(--page-pop-hover); }
|
|
128
141
|
.page-tool-turn { gap: 4px; padding: 0 8px; }
|
|
142
|
+
/* The phone's fourth control: everything the narrow bar folds away. */
|
|
143
|
+
.page-tool-more { display: none; }
|
|
129
144
|
.page-tool-sep { width: 1px; height: 16px; margin: 0 3px; background: currentColor; opacity: 0.5; }
|
|
145
|
+
.page-safe-probe { position: fixed; left: 0; bottom: 0; width: 0; height: 0; padding-bottom: env(safe-area-inset-bottom); visibility: hidden; pointer-events: none; }
|
|
130
146
|
.page-plus { position: absolute; z-index: 2; top: calc(var(--page-plus-top, 0px) - var(--page-plus-size) / 2); left: calc(var(--page-plus-left, 0px) - var(--page-plus-size) - 12px); width: var(--page-plus-size); height: var(--page-plus-size); display: grid; place-items: center; padding: 0; cursor: pointer; }
|
|
131
147
|
.page-plus[aria-expanded="true"] { color: var(--page-chrome-ink-moved); background: var(--page-chrome-bg-moved); }
|
|
132
148
|
.page-grip { position: absolute; z-index: 2; top: calc(var(--page-plus-top, 0px) - var(--page-grip-size, var(--page-plus-size)) / 2); left: calc(var(--page-plus-left, 0px) - var(--page-plus-size) - 12px - var(--page-grip-size, var(--page-plus-size)) - 2px); width: var(--page-grip-size, var(--page-plus-size)); height: var(--page-grip-size, var(--page-plus-size)); display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--page-chrome-radius); background: none; color: var(--page-chrome-ink-moved); opacity: 0; cursor: grab; touch-action: none; transition: opacity var(--page-fade); }
|
|
@@ -157,40 +173,8 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
|
|
|
157
173
|
.page-link-btn { display: inline-grid; place-items: center; min-width: 30px; height: 30px; padding: 0 6px; border: 0; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
|
|
158
174
|
.page-link-btn:hover { background: var(--page-pop-hover); }
|
|
159
175
|
.page-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
160
|
-
.page-editor .page-image { margin-inline: 0; }
|
|
161
|
-
.page-image-frame { position: relative; margin: 0 auto; max-width: 100%; }
|
|
162
|
-
.page-image-frame img { display: block; width: 100%; height: auto; border-radius: var(--page-image-radius, 6px); }
|
|
163
|
-
.page-image[data-broken] .page-image-frame { min-height: 80px; background: var(--page-code-bg); border-radius: var(--page-image-radius, 6px); }
|
|
164
|
-
.page-image-handle { position: absolute; top: 50%; inset-inline-end: 6px; width: 6px; height: 36px; margin-top: -18px; border-radius: 3px; background: var(--page-chrome-ink-moved); opacity: 0; cursor: ew-resize; touch-action: none; transition: opacity var(--page-fade); }
|
|
165
|
-
.page-image:hover .page-image-handle, .page-image.is-selected .page-image-handle { opacity: 1; }
|
|
166
|
-
.page-editor .page-image.page-block-selected { background: none; box-shadow: none; }
|
|
167
|
-
.page-editor .page-image.page-block-selected .page-image-frame { box-shadow: 0 0 0 6px var(--page-block-selected, rgba(88, 120, 170, 0.12)); border-radius: var(--page-image-radius, 6px); }
|
|
168
|
-
.page-image[data-align="left"] .page-image-frame { margin-left: 0; margin-right: auto; }
|
|
169
|
-
.page-image[data-align="right"] .page-image-frame { margin-left: auto; margin-right: 0; }
|
|
170
|
-
.page-image[data-align="left"] .page-image-caption { text-align: left; }
|
|
171
|
-
.page-image[data-align="right"] .page-image-caption { text-align: right; }
|
|
172
|
-
.page-image-align { position: absolute; top: 8px; inset-inline-end: 8px; display: none; gap: 1px; padding: 2px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); box-shadow: var(--page-pop-shadow); border-radius: 7px; }
|
|
173
|
-
.page-image.is-selected .page-image-align, .page-image.page-block-selected .page-image-align { display: flex; }
|
|
174
|
-
.page-image-align button { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 5px; background: none; color: inherit; cursor: pointer; }
|
|
175
|
-
.page-image-align button:hover, .page-image-align button[aria-pressed="true"] { background: var(--page-pop-hover); }
|
|
176
|
-
.page-image.is-selected .page-image-frame img { outline: 2px solid var(--page-selection); outline-offset: 2px; }
|
|
177
|
-
.page-image-caption { margin-top: 6px; color: var(--page-ink-soft); font-size: 0.88em; text-align: center; outline: none; }
|
|
178
|
-
.page-image-caption[data-empty]::before { content: attr(data-placeholder); color: var(--page-ink-faint); }
|
|
179
176
|
.ProseMirror-focused .page-placeholder::before { content: attr(data-placeholder); float: left; height: 0; color: var(--page-ink-faint); pointer-events: none; }
|
|
180
|
-
.page-
|
|
181
|
-
.page-file-link { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border: 1px solid var(--page-rule); border-radius: 8px; color: var(--page-ink); text-decoration: none; }
|
|
182
|
-
.page-file-icon { color: var(--page-link, var(--page-accent)); }
|
|
183
|
-
.page-file-name { color: var(--page-link, var(--page-accent)); min-width: 0; overflow-wrap: anywhere; }
|
|
184
|
-
.page-file-meta { margin-inline-start: auto; color: var(--page-ink-faint); font-size: 0.88em; }
|
|
185
|
-
.page-upload { display: flex; align-items: center; gap: 10px; margin: var(--page-block-gap) 0; padding: 8px 12px; border: 1px dashed var(--page-rule); border-radius: 8px; color: var(--page-ink-soft); font-size: 0.88em; }
|
|
186
|
-
.page-upload-preview { width: 40px; height: 40px; object-fit: cover; border-radius: 4px; opacity: 0.6; }
|
|
187
|
-
.page-upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
188
|
-
.page-upload-bar { flex: 1; height: 2px; border-radius: 1px; background: var(--page-rule); overflow: hidden; }
|
|
189
|
-
.page-upload-bar > span { display: block; height: 100%; background: var(--page-progress, var(--page-ink-soft)); transition: width 200ms; }
|
|
190
|
-
.page-upload[data-state="error"] .page-upload-msg, .page-upload[data-state="embed-choice"] .page-upload-msg { flex: 1; }
|
|
191
|
-
.page-upload[data-state="error"] .page-upload-msg { color: var(--page-danger, #9E3B2F); }
|
|
192
|
-
.page-upload-btn { border: 0; padding: 4px 8px; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
|
|
193
|
-
.page-upload-btn:hover { background: var(--page-pop-hover); }
|
|
177
|
+
.page-editor .page-title-placeholder::before { content: attr(data-placeholder); float: left; height: 0; color: var(--page-ink-faint); pointer-events: none; }
|
|
194
178
|
.page-csv-error { margin: 8px 0; color: var(--page-danger); font-size: 14px; }
|
|
195
179
|
.page-editor .page-table { max-width: 100%; min-width: 0; border: 1px solid var(--page-rule); border-radius: 8px; }
|
|
196
180
|
.page-editor .page-chart { position: relative; max-width: 100%; min-width: 0; margin-inline: 0; color: var(--page-ink); }
|
|
@@ -198,14 +182,6 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
|
|
|
198
182
|
.page-chart figcaption { color: var(--page-ink-soft); font-size: 0.88em; text-align: center; }
|
|
199
183
|
.page-chart-settings { display: block; margin: 8px auto 0; padding: 4px 10px; border: 1px solid var(--page-rule); border-radius: 6px; background: var(--page-pop-bg); color: var(--page-ink); font: inherit; cursor: pointer; }
|
|
200
184
|
.page-chart-title-input { display: block; width: min(100%, 24em); box-sizing: border-box; margin: 6px auto; padding: 4px 8px; border: 1px solid var(--page-rule); border-radius: 6px; background: var(--page-pop-bg); color: var(--page-ink); font: inherit; }
|
|
201
|
-
.page-tool-block { position: relative; min-height: 48px; border: 1px solid var(--page-rule); border-radius: 8px; padding: 12px; }
|
|
202
|
-
.page-tool-settings { position: absolute; top: 4px; inset-inline-end: 4px; border: 0; border-radius: 5px; background: var(--page-pop-bg); color: var(--page-ink-soft); font: 12px var(--page-font); padding: 5px 8px; cursor: pointer; opacity: 0; }
|
|
203
|
-
.page-tool-block:hover .page-tool-settings, .page-tool-block:focus-within .page-tool-settings { opacity: 1; }
|
|
204
|
-
.page-ai-panel { position: relative; z-index: 5; width: min(520px, 100%); box-sizing: border-box; margin: 12px auto; padding: 14px; border: 1px solid var(--page-pop-line); border-radius: 10px; background: var(--page-pop-bg); color: var(--page-pop-ink); box-shadow: var(--page-pop-shadow); }
|
|
205
|
-
.page-ai-panel textarea { width: 100%; min-height: 88px; box-sizing: border-box; padding: 8px; border: 1px solid var(--page-pop-line); border-radius: 6px; background: var(--page-pop-bg); color: inherit; font: inherit; resize: vertical; }
|
|
206
|
-
.page-ai-actions { display: flex; gap: 8px; margin-top: 8px; }
|
|
207
|
-
.page-ai-actions button { border: 1px solid var(--page-pop-line); border-radius: 6px; background: var(--page-pop-bg); color: inherit; font: inherit; padding: 5px 10px; cursor: pointer; }
|
|
208
|
-
.page-ai-status, [data-tool-note], [data-tool-error] { color: var(--page-danger); font-size: 0.85em; }
|
|
209
185
|
.page-table-scroll { max-width: 100%; overflow-x: auto; }
|
|
210
186
|
.page-table table { border-collapse: collapse; min-width: max-content; width: 100%; table-layout: auto; }
|
|
211
187
|
.page-table th, .page-table td { min-width: 120px; padding: 6px 10px; border-inline-end: 1px solid var(--page-rule); border-block-end: 1px solid var(--page-rule); text-align: start; white-space: pre-wrap; }
|
|
@@ -223,18 +199,31 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
|
|
|
223
199
|
.page-check-popup { display: inline-flex; gap: 4px; padding: 4px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); border-radius: var(--page-pop-radius); box-shadow: var(--page-pop-shadow); }
|
|
224
200
|
.page-check-popup input { font: inherit; color: inherit; background: var(--page-pop-bg); border: 1px solid var(--page-pop-line); }
|
|
225
201
|
.page-check-popup.page-check-owner { display: inline-block; width: min(280px, 80vw); padding: 8px 0; vertical-align: top; }
|
|
202
|
+
.page-board-owners { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-inline: 8px; }
|
|
203
|
+
.page-board-people { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
204
|
+
.page-board-person { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px 0 4px; border: 1px solid var(--page-rule); border-radius: 14px; background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 13px; cursor: pointer; transition: background-color var(--page-fade), border-color var(--page-fade); }
|
|
205
|
+
.page-board-person.is-none { padding-inline-start: 10px; color: var(--page-pop-muted); }
|
|
206
|
+
.page-board-person:hover { background: var(--page-pop-hover); }
|
|
207
|
+
.page-board-person[aria-pressed="true"] { border-color: var(--page-accent); background: var(--page-selection); color: var(--page-pop-ink); }
|
|
208
|
+
.page-board-owner-input { width: 100%; height: 32px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--page-pop-line); border-radius: 7px; background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 14px; }
|
|
209
|
+
.page-board-owner-input::placeholder { color: var(--page-ink-faint); }
|
|
210
|
+
.page-board-face { flex: none; display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; background: var(--page-code-bg); box-shadow: inset 0 0 0 1px var(--page-rule); color: var(--page-ink-soft); font-size: 10.5px; font-weight: 600; line-height: 1; }
|
|
226
211
|
.page-scrim { display: none; }
|
|
227
212
|
.page-shell [hidden] { display: none !important; }
|
|
228
|
-
${TABLE_CSS}
|
|
229
|
-
${BOARD_CSS}
|
|
230
213
|
@media (max-width: 560px) {
|
|
231
|
-
|
|
214
|
+
/* One fixed bar over the visual viewport, with the four controls a phone keeps.
|
|
215
|
+
The bubble and the desktop-only tools stand down so nothing is doubled. */
|
|
216
|
+
.page-dock { display: flex; position: fixed; left: 0; top: 8px; width: 204px; min-width: 204px; box-sizing: border-box; align-items: center; justify-content: center; gap: 4px; margin: 0; padding: 8px; z-index: 3; }
|
|
217
|
+
.page-tool-desk { display: none; }
|
|
218
|
+
.page-dock .page-tool-more { display: inline-flex; }
|
|
219
|
+
.page-dock .page-tool { min-width: 44px; height: 44px; }
|
|
220
|
+
.page-shell:has(.page-bubble:not([hidden])) .page-dock { visibility: visible; }
|
|
221
|
+
.page-dock + .page-editor { padding-bottom: calc(72px + env(safe-area-inset-bottom)); }
|
|
232
222
|
.page-plus { position: fixed; top: auto; left: 16px; bottom: calc(16px + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; }
|
|
233
|
-
.page-grip { position: fixed; top: auto; left: 68px; bottom: calc(16px + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; opacity: 1; color: var(--page-chrome-ink-rest); background: var(--page-chrome-bg-rest); border: 1px solid var(--page-chrome-line-rest); }
|
|
234
|
-
.page-bubble {
|
|
235
|
-
.page-bubble .page-tool { min-width: 44px; height: 44px; }
|
|
223
|
+
.page-grip { position: fixed; top: auto; left: 68px; bottom: calc(16px + var(--page-grip-lift, 0px) + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; opacity: 1; color: var(--page-chrome-ink-rest); background: var(--page-chrome-bg-rest); border: 1px solid var(--page-chrome-line-rest); }
|
|
224
|
+
.page-bubble { display: none; }
|
|
236
225
|
.page-scrim { display: block; position: fixed; inset: 0; z-index: 4; background: var(--page-scrim); }
|
|
237
|
-
.page-menu { position: fixed; left: 0;
|
|
226
|
+
.page-menu { position: fixed; left: 0; top: 0; width: var(--page-pop-width, min(280px, calc(100vw - 16px))); max-height: min(70vh, var(--page-pop-max, 70vh)); overflow-y: auto; transform: translate(var(--page-pop-x, 0px), var(--page-pop-y, 0px)); z-index: 5; }
|
|
238
227
|
.page-menu-item { min-height: 48px; font-size: 16px; }
|
|
239
228
|
.page-link { position: fixed; left: 0; right: 0; top: auto; bottom: 0; max-width: none; padding: 8px 16px calc(8px + env(safe-area-inset-bottom)); border-radius: 0; border-width: 1px 0 0; }
|
|
240
229
|
.page-link-input { min-width: 0; height: 44px; font-size: 16px; }
|
|
@@ -242,9 +231,11 @@ ${BOARD_CSS}
|
|
|
242
231
|
.page-image-frame { width: 100% !important; }
|
|
243
232
|
.page-image-handle { display: none; }
|
|
244
233
|
.page-table th button, .page-table-row-menu, .page-check-chip, .page-check-popup button, .page-menu-item { min-height: 44px; }
|
|
234
|
+
.page-board-owner-input { font-size: 16px; height: 44px; }
|
|
235
|
+
.page-board-person { height: 44px; border-radius: 22px; }
|
|
245
236
|
}
|
|
246
237
|
@media (prefers-reduced-motion: reduce) {
|
|
247
|
-
.page-dock, .page-plus, .page-grip, .page-toggle-button, .page-image-handle, .page-upload-bar > span { transition: none; }
|
|
238
|
+
.page-dock, .page-plus, .page-grip, .page-toggle-button, .page-image-handle, .page-upload-bar > span, .page-board-person { transition: none; }
|
|
248
239
|
}
|
|
249
240
|
}
|
|
250
241
|
`;
|
|
@@ -254,6 +245,19 @@ const insertHardBreak = (st, dispatch) => {
|
|
|
254
245
|
return true;
|
|
255
246
|
};
|
|
256
247
|
|
|
248
|
+
// Mod-u belongs to an editable page selection only: anywhere else the browser
|
|
249
|
+
// keeps its own meaning for the key. The write itself goes through the format
|
|
250
|
+
// gate, so a refused activation leaves the document exactly as it was.
|
|
251
|
+
const underlineShortcut = (st, dispatch, view) => {
|
|
252
|
+
if (!dispatch || !view?.editable || !markable(st, schema.marks.underline)) return false;
|
|
253
|
+
import("./format-native-commands.js")
|
|
254
|
+
.then(({ formatMark }) => formatMark(view, "underline"), () => null)
|
|
255
|
+
.then((result) => {
|
|
256
|
+
if (result?.status !== "applied" && !view.isDestroyed) announce(view, result?.message ?? "That format isn\u2019t available here.");
|
|
257
|
+
});
|
|
258
|
+
return true;
|
|
259
|
+
};
|
|
260
|
+
|
|
257
261
|
const editKeys = ({ history: useHistory = true, undo, redo } = {}) => ({
|
|
258
262
|
"Mod-z": useHistory ? history.undo : () => { undo?.(); return true; },
|
|
259
263
|
"Shift-Mod-z": useHistory ? history.redo : () => { redo?.(); return true; },
|
|
@@ -261,32 +265,266 @@ const editKeys = ({ history: useHistory = true, undo, redo } = {}) => ({
|
|
|
261
265
|
"Shift-Enter": insertHardBreak,
|
|
262
266
|
"Mod-b": commands.toggleMark(schema.marks.strong),
|
|
263
267
|
"Mod-i": commands.toggleMark(schema.marks.em),
|
|
268
|
+
"Mod-u": underlineShortcut,
|
|
264
269
|
});
|
|
265
270
|
|
|
271
|
+
const restoreShortcut = (st, dispatch, view) => {
|
|
272
|
+
if (composing(null, view)) return false;
|
|
273
|
+
return inputrules.undoInputRule(st, dispatch && ((tr) => dispatch(asStep(tr))));
|
|
274
|
+
};
|
|
275
|
+
|
|
266
276
|
const markKeys = {
|
|
267
|
-
Backspace: commands.chainCommands(
|
|
277
|
+
Backspace: commands.chainCommands(restoreShortcut, backspaceToParagraph),
|
|
268
278
|
"Mod-e": commands.toggleMark(schema.marks.code),
|
|
269
279
|
"Mod-Shift-s": commands.toggleMark(schema.marks.strike),
|
|
270
280
|
};
|
|
271
281
|
|
|
272
282
|
const defaultUpload = (file, options) => globalThis.clay?.upload?.(file, options) ?? Promise.resolve({ ok: false, code: "unavailable", msg: "File uploads are not available in this editor" });
|
|
273
283
|
|
|
274
|
-
|
|
284
|
+
const nodeViewRegistries = new WeakMap();
|
|
285
|
+
|
|
286
|
+
export const lazyViewsKey = new state.PluginKey("lazyViews");
|
|
287
|
+
|
|
288
|
+
// Stands in for a view part's node view until its type loads: the schema's own
|
|
289
|
+
// toDOM, and an update that asks to be recreated once the real view exists.
|
|
290
|
+
class PendingView {
|
|
291
|
+
constructor(node, registry) {
|
|
292
|
+
this.node = node;
|
|
293
|
+
this.type = node.type;
|
|
294
|
+
this.registry = registry;
|
|
295
|
+
const { dom, contentDOM } = model.DOMSerializer.renderSpec(document, node.type.spec.toDOM(node));
|
|
296
|
+
this.dom = dom;
|
|
297
|
+
this.contentDOM = contentDOM ?? null;
|
|
298
|
+
}
|
|
299
|
+
update(node) { return node.type === this.type && !this.registry.has(node.type.name); }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Recreates only the node views of the types that just loaded: one node
|
|
303
|
+
// decoration makes ProseMirror rebuild those nodes and no others.
|
|
304
|
+
export function lazyViewsPlugin() {
|
|
305
|
+
return new state.Plugin({
|
|
306
|
+
key: lazyViewsKey,
|
|
307
|
+
state: {
|
|
308
|
+
init: () => pmView.DecorationSet.empty,
|
|
309
|
+
apply(tr) {
|
|
310
|
+
const meta = tr.getMeta(lazyViewsKey);
|
|
311
|
+
if (!meta) return pmView.DecorationSet.empty;
|
|
312
|
+
const decorations = [];
|
|
313
|
+
tr.doc.forEach((node, offset) => {
|
|
314
|
+
if (!meta.types.includes(node.type.name)) return;
|
|
315
|
+
decorations.push(pmView.Decoration.node(offset, offset + node.nodeSize, {}, { lazyView: decorations.length + 1 }));
|
|
316
|
+
});
|
|
317
|
+
return pmView.DecorationSet.create(tr.doc, decorations);
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
props: { decorations: (state) => lazyViewsKey.getState(state) },
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Adds a view part to a running editor: its CSS at once, its node views into the
|
|
325
|
+
// editor's registry, and a transaction that recreates only those block types.
|
|
326
|
+
export function installPageFeature({ view, upload, toolHost, board = null, undo = null, redo = null, revise = null, highlight }, id, mod) {
|
|
327
|
+
const feature = mod.createFeature({ view, upload, toolHost, board, undo, redo, revise, highlight });
|
|
328
|
+
const registry = nodeViewRegistries.get(view);
|
|
329
|
+
const apply = () => {
|
|
330
|
+
if (feature.css) styleWithLegacyId(`page-feature-${id}`, feature.css, `page-feature-${id}`, ["editor-ui"]);
|
|
331
|
+
if (!feature.nodeViews) return;
|
|
332
|
+
const types = Object.keys(feature.nodeViews);
|
|
333
|
+
for (const type of types) registry.set(type, feature.nodeViews[type]);
|
|
334
|
+
view.dispatch(view.state.tr.setMeta(lazyViewsKey, { types }).setMeta("addToHistory", false));
|
|
335
|
+
};
|
|
336
|
+
const afterComposition = () => {
|
|
337
|
+
if (view.isDestroyed) return;
|
|
338
|
+
if (view.composing) setTimeout(afterComposition, 25);
|
|
339
|
+
else apply();
|
|
340
|
+
};
|
|
341
|
+
if (view.composing) view.dom.addEventListener("compositionend", () => setTimeout(afterComposition, 25), { once: true });
|
|
342
|
+
else apply();
|
|
343
|
+
return feature;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Loads the view part for a block type the editor shows but has no view for:
|
|
347
|
+
// the blocks that were there at boot, and any that arrive later.
|
|
348
|
+
function featureWatchPlugin(features) {
|
|
349
|
+
return new state.Plugin({
|
|
350
|
+
view(editorView) {
|
|
351
|
+
const failed = new Set();
|
|
352
|
+
const walk = () => {
|
|
353
|
+
for (const id of failed) if (features.has(id)) failed.delete(id);
|
|
354
|
+
editorView.state.doc.forEach((node) => {
|
|
355
|
+
const id = featureForType(node.type.name);
|
|
356
|
+
if (!id || features.has(id) || failed.has(id)) return;
|
|
357
|
+
features.ensure(id).catch(() => { failed.add(id); });
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
let doc = editorView.state.doc;
|
|
361
|
+
walk();
|
|
362
|
+
return {
|
|
363
|
+
update() {
|
|
364
|
+
if (editorView.state.doc === doc) return;
|
|
365
|
+
doc = editorView.state.doc;
|
|
366
|
+
walk();
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// The optional commands. A command module exports `createFeature(context)` returning
|
|
374
|
+
// `{ run, dispose }`; the editor fetches it on the first ask and installs it once, and
|
|
375
|
+
// nothing here is preloaded by the block-type scan. A command is asked for with the state
|
|
376
|
+
// it was asked in: the module is fetched, and the command runs only if the edit is still
|
|
377
|
+
// open, the document revision has not moved and the selection is where it was. A failed
|
|
378
|
+
// fetch is reported and asked for again on the next request; an exit disposes what was
|
|
379
|
+
// installed and aborts whatever was still arriving, and the next entry installs fresh
|
|
380
|
+
// against the module the browser already holds.
|
|
381
|
+
function createCommandHost({ load = () => Promise.reject(new Error("page: this editor has no command module")), context = () => ({}), reportError } = {}) {
|
|
382
|
+
const modules = new Map();
|
|
383
|
+
const waiters = new Set();
|
|
384
|
+
let view = null;
|
|
385
|
+
let loader = null;
|
|
386
|
+
let active = true;
|
|
387
|
+
let disposed = false;
|
|
388
|
+
let revision = 0;
|
|
389
|
+
|
|
390
|
+
// One fetch per command for the life of the host, whatever the entry: a module that
|
|
391
|
+
// failed is the one thing not kept, so the next ask loads it again.
|
|
392
|
+
const moduleFor = (id) => {
|
|
393
|
+
let fetched = modules.get(id);
|
|
394
|
+
if (!fetched) {
|
|
395
|
+
fetched = Promise.resolve(load(id));
|
|
396
|
+
modules.set(id, fetched);
|
|
397
|
+
fetched.catch(() => { if (modules.get(id) === fetched) modules.delete(id); });
|
|
398
|
+
}
|
|
399
|
+
return fetched;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const loaderFor = () => {
|
|
403
|
+
loader ??= createFeatureLoader({
|
|
404
|
+
load: moduleFor,
|
|
405
|
+
install: (id, mod) => mod.createFeature(context(view)),
|
|
406
|
+
reportError: reportError ?? ((error, id) => console.error(`clayjs: the ${id} command failed to load`, error)),
|
|
407
|
+
});
|
|
408
|
+
return loader;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// The document revision and the selection a command was asked for. A revision moves
|
|
412
|
+
// when any transaction changes the document, whoever wrote it.
|
|
413
|
+
const leaseOf = () => {
|
|
414
|
+
const at = revision;
|
|
415
|
+
const selection = view.state.selection;
|
|
416
|
+
return {
|
|
417
|
+
revision: at,
|
|
418
|
+
selection,
|
|
419
|
+
valid: () => active && !disposed && !view.isDestroyed && revision === at && view.state.selection.eq(selection),
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
function observe(state, prevState) {
|
|
424
|
+
if (state.doc !== prevState.doc) revision += 1;
|
|
425
|
+
for (const waiter of waiters) {
|
|
426
|
+
if (waiter.lease.valid()) continue;
|
|
427
|
+
waiters.delete(waiter);
|
|
428
|
+
waiter.controller.abort({ ok: false, code: "stale-target" });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async function run(id, args) {
|
|
433
|
+
if (!active || disposed || !view || view.isDestroyed) return { ok: false, code: "not-editing" };
|
|
434
|
+
const waiter = { lease: leaseOf(), controller: new AbortController() };
|
|
435
|
+
waiters.add(waiter);
|
|
436
|
+
try {
|
|
437
|
+
const feature = await loaderFor().ensure(id, { signal: waiter.controller.signal });
|
|
438
|
+
if (!feature) return { ok: false, code: "not-editing" };
|
|
439
|
+
if (!waiter.lease.valid()) return { ok: false, code: "stale-target" };
|
|
440
|
+
return await feature.run(args);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
if (waiter.controller.signal.aborted) return waiter.controller.signal.reason;
|
|
443
|
+
return { ok: false, code: "unavailable", error };
|
|
444
|
+
} finally {
|
|
445
|
+
waiters.delete(waiter);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function leave() {
|
|
450
|
+
if (!active) return;
|
|
451
|
+
active = false;
|
|
452
|
+
for (const waiter of waiters) waiter.controller.abort({ ok: false, code: "not-editing" });
|
|
453
|
+
waiters.clear();
|
|
454
|
+
loader?.dispose();
|
|
455
|
+
loader = null;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
attach(editorView) { view = editorView; },
|
|
460
|
+
observe,
|
|
461
|
+
run,
|
|
462
|
+
enter() { if (!disposed) active = true; },
|
|
463
|
+
leave,
|
|
464
|
+
dispose() {
|
|
465
|
+
leave();
|
|
466
|
+
disposed = true;
|
|
467
|
+
active = false;
|
|
468
|
+
modules.clear();
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const commandHosts = new WeakMap();
|
|
474
|
+
// A command asks for its module with the state it was asked in, so the host has to hear
|
|
475
|
+
// about every transaction: one that moves the document revision or the selection refuses
|
|
476
|
+
// a command whose module is still arriving rather than running it where the person has
|
|
477
|
+
// since left. It also holds the one typed key a command owns: `@` asks for the mention
|
|
478
|
+
// picker, and the host types the character itself so the command is handed the bookmark it
|
|
479
|
+
// belongs to, with everything else about that picker left to the module that arrives for
|
|
480
|
+
// it. It is asked before the plugin that records ordinary typing, since it types it.
|
|
481
|
+
function commandWatchPlugin(host) {
|
|
482
|
+
return new state.Plugin({
|
|
483
|
+
view: () => ({ update: (editorView, prevState) => host.observe(editorView.state, prevState) }),
|
|
484
|
+
props: { handleTextInput(v, from, to, text) {
|
|
485
|
+
if (text !== "@" || v.composing || !Object.hasOwn(COMMAND_FEATURES, "mentions")) return false;
|
|
486
|
+
v.dispatch(v.state.tr.insertText("@", from, to));
|
|
487
|
+
host.run("mentions", { from, to: from + 1 });
|
|
488
|
+
return true;
|
|
489
|
+
} },
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Runs an optional command on a mounted editor, by the id its row has in COMMAND_FEATURES.
|
|
494
|
+
// A command nobody offers, or an editor with no command host, refuses rather than throws.
|
|
495
|
+
export function runPageCommand(view, id, args) {
|
|
496
|
+
const host = view ? commandHosts.get(view) : null;
|
|
497
|
+
if (!host) return Promise.resolve({ ok: false, code: "not-editing" });
|
|
498
|
+
if (!Object.hasOwn(COMMAND_FEATURES, id)) return Promise.resolve({ ok: false, code: "unknown-command" });
|
|
499
|
+
return host.run(id, args);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Tools follow the tables a document holds; the host arrives with the tool view
|
|
503
|
+
// part on a document that had no tool at boot.
|
|
504
|
+
function toolFollowPlugin(toolHost) {
|
|
505
|
+
return new state.Plugin({
|
|
506
|
+
view(view) {
|
|
507
|
+
const changed = toolHost ? () => toolHost.tablesChanged() : () => toolViews.get(view)?.tablesChanged?.();
|
|
508
|
+
let previous = view.state.doc;
|
|
509
|
+
return { update(next) { if (next.state.doc !== previous) { previous = next.state.doc; changed(); } } };
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function pagePlugins({ upload = defaultUpload, highlight, history: useHistory = true, undo, redo, onAiTool = null, toolHost = null, features = null, commandHost = null } = {}) {
|
|
275
515
|
return [
|
|
276
516
|
announcementPlugin(),
|
|
277
517
|
chromePlugin({ onAiTool }),
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return { update(next) { if (next.state.doc !== previous) { previous = next.state.doc; toolHost.tablesChanged(); } } };
|
|
282
|
-
},
|
|
283
|
-
}),
|
|
518
|
+
commandHost ? commandWatchPlugin(commandHost) : null,
|
|
519
|
+
slashRangePlugin(),
|
|
520
|
+
toolFollowPlugin(toolHost),
|
|
284
521
|
linkPlugin(),
|
|
285
522
|
tableSelectPlugin(),
|
|
286
523
|
blockSelectionPlugin(),
|
|
287
524
|
markdownPlugin(),
|
|
288
525
|
markdownKeys(),
|
|
289
526
|
placeholderPlugin(),
|
|
527
|
+
titlePlugin(),
|
|
290
528
|
keymap.keymap(markKeys),
|
|
291
529
|
tableKeys,
|
|
292
530
|
checklistKeys,
|
|
@@ -296,16 +534,18 @@ export function pagePlugins({ upload = defaultUpload, highlight, history: useHis
|
|
|
296
534
|
structurePlugin(),
|
|
297
535
|
identityPlugin(),
|
|
298
536
|
tablePlugin(),
|
|
299
|
-
|
|
300
|
-
boardPlugin(),
|
|
537
|
+
viewHookPlugin(),
|
|
301
538
|
checklistPlugin(),
|
|
302
539
|
csvImportPlugin({ upload }),
|
|
303
540
|
collapsePlugin(),
|
|
304
|
-
mediaPlugin({ upload }),
|
|
305
|
-
|
|
541
|
+
mediaPlugin({ upload, features }),
|
|
542
|
+
highlightSlot(),
|
|
306
543
|
pastePlugin(),
|
|
544
|
+
historyIntentPlugin(),
|
|
307
545
|
useHistory ? history.history() : null,
|
|
308
546
|
gapcursor.gapCursor(),
|
|
547
|
+
lazyViewsPlugin(),
|
|
548
|
+
features ? featureWatchPlugin(features) : null,
|
|
309
549
|
].filter(Boolean);
|
|
310
550
|
}
|
|
311
551
|
|
|
@@ -315,6 +555,29 @@ export function pageState(blocks, options = {}) {
|
|
|
315
555
|
return state.EditorState.create({ doc, plugins: pagePlugins(options) });
|
|
316
556
|
}
|
|
317
557
|
|
|
558
|
+
// The shell is built detached and moved into the article once it is complete, so
|
|
559
|
+
// the node views in it measured nothing while it was out of the document. One pass
|
|
560
|
+
// in place gives them a real box: the state is the same object it was, and no node
|
|
561
|
+
// view is rebuilt for it.
|
|
562
|
+
export function refreshMountedView(view) {
|
|
563
|
+
if (!view || view.isDestroyed) return;
|
|
564
|
+
view.updateState(view.state);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Admission on a mounted editor, in one place: the commit that enters edit opens it,
|
|
568
|
+
// and the exit that holds the session closed shuts it without touching a buffer.
|
|
569
|
+
export function setEditorActive(view, active) {
|
|
570
|
+
if (!view || view.isDestroyed) return false;
|
|
571
|
+
const open = active === true;
|
|
572
|
+
view.setProps({ editable: () => open });
|
|
573
|
+
// A refused exit closes admission without destroying the view, so the commands are told
|
|
574
|
+
// here: the exit disposes what they installed, and the next entry installs fresh.
|
|
575
|
+
const host = commandHosts.get(view);
|
|
576
|
+
if (open) host?.enter();
|
|
577
|
+
else host?.leave();
|
|
578
|
+
return open;
|
|
579
|
+
}
|
|
580
|
+
|
|
318
581
|
// Clicking a checklist box (drawn by ::before in the item's start gutter)
|
|
319
582
|
// toggles `checked`.
|
|
320
583
|
function checkboxClick(view, event) {
|
|
@@ -335,9 +598,8 @@ function checkboxClick(view, event) {
|
|
|
335
598
|
// Mounts the editor in `mount`, inside a .page-shell wrapper (the surface a
|
|
336
599
|
// theme styles). `onChange(record)` gets the block record (blockMap) after
|
|
337
600
|
// every transaction that changed the document.
|
|
338
|
-
export function createPageEditor(mount, { blocks = null, onChange = null, upload, highlight, doc: initialDoc, dispatch, history: useHistory = true, undo, redo, editable = true, toolHost
|
|
601
|
+
export function createPageEditor(mount, { blocks = null, onChange = null, upload, highlight, doc: initialDoc, dispatch, history: useHistory = true, undo, redo, editable = true, toolHost = null, onAiTool = null, reviseTool = null, board = null, features: providedFeatures = null, formatGate = null } = {}) {
|
|
339
602
|
const doc = mount.ownerDocument;
|
|
340
|
-
const toolHost = providedToolHost ?? createToolHost({ mode: "edit" });
|
|
341
603
|
// Editor chrome, injected on every edit-mode boot and kept out of the saved file:
|
|
342
604
|
// the reader theme <link> styles the page for visitors.
|
|
343
605
|
styleWithLegacyId(STYLE_ID, PAGE_EDITOR_CSS, STYLE_ID, ["editor-ui"]);
|
|
@@ -347,20 +609,30 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
|
|
|
347
609
|
shell.setAttribute("sap-ignore", "");
|
|
348
610
|
mount.appendChild(shell);
|
|
349
611
|
let afterStep = false;
|
|
350
|
-
|
|
351
|
-
|
|
612
|
+
let view = null;
|
|
613
|
+
const features = providedFeatures ?? createFeatureLoader({
|
|
614
|
+
load: (id) => FEATURES[id].load(),
|
|
615
|
+
install: (id, mod) => installPageFeature({ view, upload, toolHost, board, undo, redo, revise: reviseTool, highlight }, id, mod),
|
|
616
|
+
});
|
|
617
|
+
// Commands are their own loader: a document never fetches a picker to be read or typed
|
|
618
|
+
// in, and the host outlives a refused exit so a re-entry installs once more.
|
|
619
|
+
const commandHost = createCommandHost({
|
|
620
|
+
load: (id) => COMMAND_FEATURES[id].load(),
|
|
621
|
+
context: (editorView) => ({ view: editorView, upload }),
|
|
622
|
+
});
|
|
623
|
+
const nodeViewRegistry = new Map();
|
|
624
|
+
const nodeViews = {};
|
|
625
|
+
for (const feature of Object.values(FEATURES)) for (const type of feature.views ?? feature.types) {
|
|
626
|
+
nodeViews[type] = (node, v, getPos, decorations, inner) => {
|
|
627
|
+
const make = nodeViewRegistry.get(node.type.name);
|
|
628
|
+
return make ? make(node, v, getPos, decorations, inner) : new PendingView(node, nodeViewRegistry);
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
view = new pmView.EditorView(shell, {
|
|
632
|
+
state: pageState(blocks, { upload, highlight, doc: initialDoc, history: useHistory, undo, redo, onAiTool, toolHost, features, commandHost }),
|
|
352
633
|
attributes: { class: "page-editor" },
|
|
353
634
|
editable: () => editable,
|
|
354
|
-
nodeViews
|
|
355
|
-
image: (node, v, getPos) => new ImageView(node, v, getPos),
|
|
356
|
-
file: (node, v) => new FileView(node, v),
|
|
357
|
-
code_block: (node, v, getPos) => new CodeBlockView(node, v, getPos),
|
|
358
|
-
table: (node, v, getPos) => new TableView(node, v, getPos),
|
|
359
|
-
chart: (node, v, getPos) => new ChartView(node, v, getPos),
|
|
360
|
-
tool: (node, v, getPos) => new ToolView(node, v, getPos, { host: toolHost, revise: reviseTool }),
|
|
361
|
-
table_cell: (node, v, getPos) => new TableCellView(node, v, getPos),
|
|
362
|
-
...(board ? { board: (node, v, getPos) => new BoardView(node, v, getPos, { commands: board, undo, redo, panel: cardPanel(v) }) } : {}),
|
|
363
|
-
},
|
|
635
|
+
nodeViews,
|
|
364
636
|
handleDOMEvents: {
|
|
365
637
|
mousedown: checkboxClick,
|
|
366
638
|
dragstart(v, event) {
|
|
@@ -382,5 +654,42 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
|
|
|
382
654
|
if (onChange && view.state.doc !== before) onChange(blockMap(view.state.doc));
|
|
383
655
|
},
|
|
384
656
|
});
|
|
657
|
+
nodeViewRegistries.set(view, nodeViewRegistry);
|
|
658
|
+
viewFeatures.set(view, features);
|
|
659
|
+
commandHost.attach(view);
|
|
660
|
+
commandHosts.set(view, commandHost);
|
|
661
|
+
const destroy = view.destroy.bind(view);
|
|
662
|
+
// A new-format command asks the format gate, which the page plugin hands over as a
|
|
663
|
+
// loader of its own: neither the gate nor its host adapter is on the editor's ready
|
|
664
|
+
// path, so a reader who never uses a newer format never fetches them. The gate is
|
|
665
|
+
// created once, on the first command, and belongs to this view.
|
|
666
|
+
let gateHeld = null;
|
|
667
|
+
let disposed = false;
|
|
668
|
+
const heldGate = () => {
|
|
669
|
+
if (!gateHeld) {
|
|
670
|
+
gateHeld = Promise.resolve().then(() => formatGate()).catch((error) => {
|
|
671
|
+
gateHeld = null;
|
|
672
|
+
throw error;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
return gateHeld;
|
|
676
|
+
};
|
|
677
|
+
view.runFormatCommand = async (apply) => {
|
|
678
|
+
if (disposed) return { status: "disposed", retry: false };
|
|
679
|
+
if (!formatGate) return { status: "unsupported-host", code: "page-format-unsupported", retry: false };
|
|
680
|
+
const gate = await heldGate();
|
|
681
|
+
if (disposed) {
|
|
682
|
+
gate.dispose?.();
|
|
683
|
+
return { status: "disposed", retry: false };
|
|
684
|
+
}
|
|
685
|
+
return gate.run({ apply });
|
|
686
|
+
};
|
|
687
|
+
view.destroy = () => {
|
|
688
|
+
disposed = true;
|
|
689
|
+
if (gateHeld) gateHeld.then((gate) => gate?.dispose?.(), () => {});
|
|
690
|
+
commandHost.dispose();
|
|
691
|
+
features.dispose();
|
|
692
|
+
destroy();
|
|
693
|
+
};
|
|
385
694
|
return view;
|
|
386
695
|
}
|