@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
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Typing joins one undo item the way it does in other editors: Mod-z removes the last
|
|
2
|
+
// word typed, not the last letter. Only the editor's own input path marks a
|
|
3
|
+
// transaction; a command or a programmatic dispatch stays one undo item by itself.
|
|
4
|
+
import { state } from "../vendor/prosemirror.vendor.js";
|
|
5
|
+
import { STEP } from "./blocks.js";
|
|
6
|
+
|
|
7
|
+
export const HISTORY_INTENT = "page-history-intent";
|
|
8
|
+
export const GROUP_MS = 500;
|
|
9
|
+
|
|
10
|
+
const blockIdAt = (doc, pos) => {
|
|
11
|
+
const $pos = doc.resolve(pos);
|
|
12
|
+
return $pos.depth ? $pos.node(1).attrs.id : null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const markKey = (st) => (st.storedMarks ?? st.selection.$from.marks()).map((m) => `${m.type.name}:${JSON.stringify(m.attrs)}`).sort().join(",");
|
|
16
|
+
|
|
17
|
+
export function typingIntent(st, from, to, text) {
|
|
18
|
+
return { kind: "insert", blockId: blockIdAt(st.doc, from), from, end: from + text.length, replaced: from !== to, text, marks: markKey(st), at: Date.now() };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function continuesGroup(prev, next) {
|
|
22
|
+
if (!prev || !next || prev.kind !== next.kind) return false;
|
|
23
|
+
if (next.kind === "compose") return prev.id === next.id;
|
|
24
|
+
if (prev.blockId !== next.blockId || next.at - prev.at > GROUP_MS) return false;
|
|
25
|
+
if (next.kind === "insert") return !next.replaced && next.from === prev.end && next.marks === prev.marks && !(/\s$/.test(prev.text) && /^\S/.test(next.text));
|
|
26
|
+
return next.direction === prev.direction && (next.direction < 0 ? next.to === prev.from : next.from === prev.from);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DELETES = { deleteContentBackward: -1, deleteContentForward: 1 };
|
|
30
|
+
const pendingDeletes = new WeakMap();
|
|
31
|
+
const PENDING_MS = 1000;
|
|
32
|
+
export const DERIVED = "page-derived";
|
|
33
|
+
|
|
34
|
+
// A native deletion arrives as a DOM change with no handler, sometimes after a delayed flush
|
|
35
|
+
// (Android waits 20 ms), so the beforeinput that announced it is held with the document it saw
|
|
36
|
+
// and claimed by the next document change, whatever that change turns out to be.
|
|
37
|
+
export function takePendingDelete(view) {
|
|
38
|
+
const mark = pendingDeletes.get(view) ?? null;
|
|
39
|
+
pendingDeletes.delete(view);
|
|
40
|
+
return mark;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function deletionIntent(tr, mark) {
|
|
44
|
+
if (!mark || tr.before !== mark.doc || Date.now() - mark.at > PENDING_MS || tr.getMeta(STEP) || tr.steps.length !== 1) return null;
|
|
45
|
+
const step = tr.steps[0];
|
|
46
|
+
if (step.slice?.size !== 0 || step.from === step.to) return null;
|
|
47
|
+
const $from = tr.before.resolve(step.from), $to = tr.before.resolve(step.to);
|
|
48
|
+
if (!$from.parent.isTextblock || !$from.sameParent($to)) return null;
|
|
49
|
+
return { kind: "delete", blockId: $from.depth ? $from.node(1).attrs.id : null, direction: mark.direction, from: step.from, to: step.to, at: mark.at };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function intentOf(transactions, view = null) {
|
|
53
|
+
const pending = view ? takePendingDelete(view) : null;
|
|
54
|
+
const primary = transactions.filter((t) => !t.getMeta(DERIVED));
|
|
55
|
+
if (primary.length !== 1) return null;
|
|
56
|
+
const tr = primary[0];
|
|
57
|
+
const composition = tr.getMeta("composition");
|
|
58
|
+
if (composition != null) return { kind: "compose", id: composition, at: Date.now() };
|
|
59
|
+
return tr.getMeta(HISTORY_INTENT) ?? deletionIntent(tr, pending);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function historyIntentPlugin() {
|
|
63
|
+
return new state.Plugin({
|
|
64
|
+
props: {
|
|
65
|
+
handleTextInput(view, from, to, text, deflt) {
|
|
66
|
+
view.dispatch(deflt().setMeta(HISTORY_INTENT, typingIntent(view.state, from, to, text)));
|
|
67
|
+
return true;
|
|
68
|
+
},
|
|
69
|
+
handleDOMEvents: {
|
|
70
|
+
beforeinput(view, event) {
|
|
71
|
+
const direction = DELETES[event.inputType];
|
|
72
|
+
if (direction) pendingDeletes.set(view, { direction, at: Date.now(), doc: view.state.doc });
|
|
73
|
+
return false;
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
package/src/page/inline.js
CHANGED
|
@@ -12,37 +12,43 @@
|
|
|
12
12
|
// "@" and the node's JSON. No mark key starts with "@", so a U+FFFC typed as
|
|
13
13
|
// text stays text. Private-use characters typed as text need nothing here:
|
|
14
14
|
// encodeForMerge (replay.js) gives each one a code of its own.
|
|
15
|
+
// A mark this schema has no type for keeps its token or named descriptor
|
|
16
|
+
// instead of throwing (format-codec.js): the unit's key is written back exactly
|
|
17
|
+
// as it was read. A mark of this schema with no compact code of its own is
|
|
18
|
+
// written as a named descriptor too, x[name, attrs], so a new mark never takes a
|
|
19
|
+
// letter an older writer already reads.
|
|
15
20
|
import { model } from "../vendor/prosemirror.vendor.js";
|
|
21
|
+
import { descriptors, entriesOf, isOpaque, keyOf, opaqueMark } from "./format-codec.js";
|
|
22
|
+
import { nativeAttrs } from "./format-native.js";
|
|
16
23
|
|
|
17
24
|
export const ATOM = "";
|
|
18
25
|
export const CODES = { strong: "b", em: "i", strike: "s", code: "c", link: "a" };
|
|
26
|
+
const NATIVE = new Set(["underline", "text_color", "highlight"]);
|
|
19
27
|
const NAMES = Object.fromEntries(Object.entries(CODES).map(([name, code]) => [code, name]));
|
|
20
28
|
|
|
21
29
|
export function markKey(marks) {
|
|
22
30
|
let key = "";
|
|
31
|
+
const named = [];
|
|
23
32
|
for (const mark of marks) {
|
|
33
|
+
if (isOpaque(mark)) {
|
|
34
|
+
key += keyOf(mark.attrs.entries);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
24
37
|
const code = CODES[mark.type.name];
|
|
25
|
-
if (!code)
|
|
38
|
+
if (!code) {
|
|
39
|
+
// A mark with no compact letter travels as a named descriptor, after the codes
|
|
40
|
+
// and in the codec's order: one of the four formats only with attrs it writes,
|
|
41
|
+
// any other (a mention, a date) with its own attrs.
|
|
42
|
+
const native = nativeAttrs(mark.type.name, mark.attrs);
|
|
43
|
+
if (!native && NATIVE.has(mark.type.name)) throw new TypeError(`page: the mark ${mark.type.name} has no key code`);
|
|
44
|
+
named.push({ name: mark.type.name, attrs: native ?? mark.attrs });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
26
47
|
key += code;
|
|
27
48
|
const names = Object.keys(mark.type.attrs);
|
|
28
49
|
if (names.length) key += JSON.stringify(names.map((name) => mark.attrs[name]));
|
|
29
50
|
}
|
|
30
|
-
return key;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// The end of the JSON array that starts at s[i].
|
|
34
|
-
function arrayEnd(s, i) {
|
|
35
|
-
let depth = 0, str = false;
|
|
36
|
-
for (; i < s.length; i++) {
|
|
37
|
-
const ch = s[i];
|
|
38
|
-
if (str) {
|
|
39
|
-
if (ch === "\\") i++;
|
|
40
|
-
else if (ch === '"') str = false;
|
|
41
|
-
} else if (ch === '"') str = true;
|
|
42
|
-
else if (ch === "[") depth++;
|
|
43
|
-
else if (ch === "]" && --depth === 0) return i + 1;
|
|
44
|
-
}
|
|
45
|
-
throw new SyntaxError(`page: unterminated attrs in mark key ${s}`);
|
|
51
|
+
return named.length ? key + keyOf(named) : key;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
const parsed = new WeakMap();
|
|
@@ -54,19 +60,30 @@ export function marksOf(schema, key) {
|
|
|
54
60
|
let marks = cache.get(key);
|
|
55
61
|
if (marks) return marks;
|
|
56
62
|
marks = [];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
const opaque = [];
|
|
64
|
+
for (const desc of descriptors(key)) {
|
|
65
|
+
// A named descriptor this schema does have a type for is that mark, not an opaque
|
|
66
|
+
// entry: one of the four formats only with attrs it writes, any other mark only
|
|
67
|
+
// with attributes its type declares. Anything else stays opaque, so nothing is lost.
|
|
68
|
+
if (desc.code === undefined) {
|
|
69
|
+
const native = nativeAttrs(desc.name, desc.attrs);
|
|
70
|
+
const type = schema.marks[desc.name];
|
|
71
|
+
if (native) marks = type.create(native).addToSet(marks);
|
|
72
|
+
else if (type && !NATIVE.has(desc.name) && Object.keys(desc.attrs ?? {}).every((field) => Object.hasOwn(type.attrs, field))) marks = type.create(desc.attrs).addToSet(marks);
|
|
73
|
+
else opaque.push(desc);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const name = NAMES[desc.code];
|
|
77
|
+
if (!name) {
|
|
78
|
+
opaque.push(desc);
|
|
79
|
+
continue;
|
|
67
80
|
}
|
|
68
|
-
|
|
81
|
+
const coded = schema.marks[name];
|
|
82
|
+
if (!coded) throw new TypeError(`page: unknown mark code ${desc.code} in ${key}`);
|
|
83
|
+
const attrs = desc.attrs ? Object.fromEntries(Object.keys(coded.attrs).map((field, j) => [field, desc.attrs[j]])) : null;
|
|
84
|
+
marks = coded.create(attrs).addToSet(marks);
|
|
69
85
|
}
|
|
86
|
+
if (opaque.length) marks = opaqueMark(entriesOf(opaque)).addToSet(marks);
|
|
70
87
|
cache.set(key, marks);
|
|
71
88
|
return marks;
|
|
72
89
|
}
|
package/src/page/items.js
CHANGED
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
import { state } from "../vendor/prosemirror.vendor.js";
|
|
6
6
|
import { schema } from "./schema.js";
|
|
7
7
|
import { isKey, keyBetween, newId } from "./model.js";
|
|
8
|
+
import { isOpaque, opaqueMark } from "./format-codec.js";
|
|
8
9
|
import { outline, subtreeRange } from "./blocks.js";
|
|
9
10
|
import { asStep } from "./blocksel.js";
|
|
10
11
|
import { pickFiles, hostPlace } from "./media.js";
|
|
11
12
|
import { makeTable, insertTableNode } from "./table.js";
|
|
12
13
|
import { pickCsv } from "./csv-import.js";
|
|
13
14
|
import { makeBoard, insertBoardNode } from "./board.js";
|
|
14
|
-
import {
|
|
15
|
+
import { chartTables } from "./chart-tables.js";
|
|
16
|
+
import { viewFeatures } from "./view-hooks.js";
|
|
15
17
|
|
|
16
18
|
const { TextSelection } = state;
|
|
17
19
|
const N = schema.nodes;
|
|
@@ -26,6 +28,7 @@ export const BLOCK_TYPES = [
|
|
|
26
28
|
{ key: "check", label: "Checklist", description: "Tasks to tick off", icon: "☐", type: "list_item", attrs: { kind: "check" }, hint: "[]", aliases: ["todo", "to-do", "task", "check", "checkbox"] },
|
|
27
29
|
{ key: "toggle", label: "Toggle", description: "Hide details under a line", icon: "▸", type: "toggle", attrs: { open: true }, hint: ">", aliases: ["collapse", "details", "fold"] },
|
|
28
30
|
{ key: "callout", label: "Callout", description: "Make a note stand out", icon: "!", type: "callout", aliases: ["note", "aside", "info"] },
|
|
31
|
+
{ key: "quote", label: "Quote", description: "Set text apart as a quotation", icon: "\u201c", type: "quote", hint: "\"", aliases: ["blockquote", "quotation", "cite"] },
|
|
29
32
|
{ key: "code", label: "Code", description: "Code with highlighting", icon: "</>", type: "code_block", hint: "```", aliases: ["pre", "snippet"] },
|
|
30
33
|
];
|
|
31
34
|
|
|
@@ -40,6 +43,10 @@ export const INSERT_ITEMS = [
|
|
|
40
43
|
|
|
41
44
|
export const CHART_ITEM = { key: "chart", label: "Chart", description: "A bar or line chart from a table", icon: "📊", aliases: ["graph", "plot", "bar", "line"], insert: "chart" };
|
|
42
45
|
|
|
46
|
+
// The chart module and its view part load together, before the block draws: a
|
|
47
|
+
// document that never takes a chart never fetches either.
|
|
48
|
+
export const loadChartPart = (view) => (viewFeatures.get(view)?.ensure("chart") ?? Promise.resolve()).catch(() => {}).then(() => import("./chart.js"));
|
|
49
|
+
|
|
43
50
|
export const MENU_ITEMS = [...BLOCK_TYPES, ...INSERT_ITEMS];
|
|
44
51
|
|
|
45
52
|
export const insertItemsFor = (st, items = MENU_ITEMS) => (chartTables(st.doc).length ? [...items, CHART_ITEM] : items);
|
|
@@ -67,8 +74,29 @@ export function retype(tr, index, item) {
|
|
|
67
74
|
return tr.setBlockType(at + 1, at + node.nodeSize - 1, N[item.type], { ...keep, ...(item.attrs || {}) });
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
// A command this runtime cannot carry out on the content it was given: the
|
|
78
|
+
// document and the clipboard keep what they hold.
|
|
79
|
+
export const REFUSED = Object.freeze({ status: "refused", code: "unsupported-format" });
|
|
80
|
+
|
|
81
|
+
const opaqueType = opaqueMark([]).type;
|
|
82
|
+
const holdsOpaque = (node) => {
|
|
83
|
+
let found = false;
|
|
84
|
+
node.forEach((child) => { if (!found && child.marks.some(isOpaque)) found = true; });
|
|
85
|
+
return found;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// A block that is not a textblock cannot be turned into anything, and one this
|
|
89
|
+
// runtime cannot rewrite, or whose opaque marks the target type would erase, is
|
|
90
|
+
// refused rather than converted into something that lost them.
|
|
91
|
+
export function canTurnInto(node, item) {
|
|
92
|
+
if (!node.isTextblock || node.type === N.opaque_block) return false;
|
|
93
|
+
return !holdsOpaque(node) || N[item.type].allowsMarkType(opaqueType);
|
|
94
|
+
}
|
|
95
|
+
|
|
70
96
|
export function turnInto(st, index, item) {
|
|
71
|
-
|
|
97
|
+
const node = st.doc.child(index);
|
|
98
|
+
if (!node.isTextblock) return null;
|
|
99
|
+
return canTurnInto(node, item) ? retype(st.tr, index, item) : REFUSED;
|
|
72
100
|
}
|
|
73
101
|
|
|
74
102
|
// A new empty paragraph after the block at `index` and its subtree, at the
|
|
@@ -116,37 +144,62 @@ export function insertDivider(st, index, tr = st.tr) {
|
|
|
116
144
|
return tr.setSelection(TextSelection.create(tr.doc, to + 2));
|
|
117
145
|
}
|
|
118
146
|
|
|
147
|
+
// The skipped letters between a query and a candidate it can be read out of,
|
|
148
|
+
// or Infinity when it cannot. Only queries of 2 to 32 letters count, so a single
|
|
149
|
+
// letter never drags in half the menu.
|
|
150
|
+
function subsequenceCost(query, candidate) {
|
|
151
|
+
if (query.length < 2 || query.length > 32) return Infinity;
|
|
152
|
+
let at = -1;
|
|
153
|
+
let cost = 0;
|
|
154
|
+
for (const ch of query) {
|
|
155
|
+
const next = candidate.indexOf(ch, at + 1);
|
|
156
|
+
if (next < 0) return Infinity;
|
|
157
|
+
cost += next - at - 1;
|
|
158
|
+
at = next;
|
|
159
|
+
}
|
|
160
|
+
return cost;
|
|
161
|
+
}
|
|
162
|
+
|
|
119
163
|
// Exact name or alias first, then label prefix, word or alias prefix,
|
|
120
|
-
// substring
|
|
164
|
+
// substring, description word, and last a fuzzy subsequence of the label, key
|
|
165
|
+
// or aliases. Equal tiers keep the lower cost, then menu order.
|
|
121
166
|
export function rankItems(items, query) {
|
|
122
167
|
const q = query.trim().toLowerCase();
|
|
123
168
|
if (!q) return items;
|
|
124
169
|
const rank = (it) => {
|
|
125
170
|
const label = it.label.toLowerCase();
|
|
126
171
|
const names = [it.key, ...(it.aliases || []), ...(it.hint ? [it.hint] : [])].map((s) => s.toLowerCase());
|
|
127
|
-
if (label === q || names.includes(q)) return 0;
|
|
128
|
-
if (label.startsWith(q)) return 1;
|
|
129
|
-
if ([...label.split(/\s+/), ...names].some((s) => s.startsWith(q))) return 2;
|
|
130
|
-
if ([label, ...names].some((s) => s.includes(q))) return 3;
|
|
131
|
-
if (q.length >= 3 && it.description && it.description.toLowerCase().split(/[^a-z0-9]+/).some((w) => w.startsWith(q))) return 4;
|
|
132
|
-
|
|
172
|
+
if (label === q || names.includes(q)) return [0, 0];
|
|
173
|
+
if (label.startsWith(q)) return [1, 0];
|
|
174
|
+
if ([...label.split(/\s+/), ...names].some((s) => s.startsWith(q))) return [2, 0];
|
|
175
|
+
if ([label, ...names].some((s) => s.includes(q))) return [3, 0];
|
|
176
|
+
if (q.length >= 3 && it.description && it.description.toLowerCase().split(/[^a-z0-9]+/).some((w) => w.startsWith(q))) return [4, 0];
|
|
177
|
+
const cost = Math.min(...[label, it.key, ...(it.aliases || [])].map((s) => subsequenceCost(q, s.toLowerCase())));
|
|
178
|
+
return cost === Infinity ? null : [5, cost];
|
|
133
179
|
};
|
|
134
|
-
return items.map((it, order) => ({ it, order, rank: rank(it) })).filter((x) => x.rank
|
|
180
|
+
return items.map((it, order) => ({ it, order, rank: rank(it) })).filter((x) => x.rank).sort((a, b) => a.rank[0] - b.rank[0] || a.rank[1] - b.rank[1] || a.order - b.order).map((x) => x.it);
|
|
135
181
|
}
|
|
136
182
|
|
|
137
183
|
// What a pick does in the insert menu for the block at `index`. A divider and
|
|
138
184
|
// the file pickers have their own placement; a block type retypes an empty line
|
|
139
|
-
// and otherwise goes into a new block below the line's whole subtree.
|
|
185
|
+
// and otherwise goes into a new block below the line's whole subtree. A block
|
|
186
|
+
// type from a later format asks the format gate before it writes anything.
|
|
140
187
|
export function runItem(view, item, index, remove = null) {
|
|
188
|
+
if (item.type !== "quote") return insertItem(view, item, index, remove);
|
|
189
|
+
return import("./format-native-commands.js").then(({ runFormat }) => runFormat(view, () => insertItem(view, item, index, remove)));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function insertItem(view, item, index, remove = null) {
|
|
141
193
|
const st = view.state;
|
|
142
194
|
if (item.insert === "image" || item.insert === "file") return pickFiles(view, item.insert === "image" ? "image/*" : "", hostPlace(st, index), remove);
|
|
143
195
|
if (item.insert === "csv") return pickCsv(view, remove);
|
|
144
|
-
if (item.insert === "table") return insertTableNode(st, view.dispatch, makeTable(), remove);
|
|
145
|
-
if (item.insert === "board") return insertBoardNode(st, view.dispatch, makeBoard(), remove);
|
|
146
|
-
if (item.insert === "chart") return chartFromSlash(view, index, remove);
|
|
196
|
+
if (item.insert === "table") return insertTableNode(st, view.dispatch, makeTable(), remove, null, index);
|
|
197
|
+
if (item.insert === "board") return insertBoardNode(st, view.dispatch, makeBoard(), remove, index);
|
|
198
|
+
if (item.insert === "chart") return loadChartPart(view).then(({ chartFromSlash }) => chartFromSlash(view, index, remove));
|
|
147
199
|
let tr = st.tr;
|
|
148
200
|
if (remove) tr.delete(remove.from, remove.to);
|
|
149
201
|
const node = tr.doc.child(index);
|
|
202
|
+
if (node.isTextblock && node.content.size === 0 && !canTurnInto(node, item)) return REFUSED;
|
|
150
203
|
if (item.insert === "divider") tr = insertDivider(st, index, tr);
|
|
151
204
|
else if (node.isTextblock && node.content.size === 0) tr = retype(tr, index, item);
|
|
152
205
|
else {
|
package/src/page/link.js
CHANGED
|
@@ -79,6 +79,7 @@ class LinkPopover {
|
|
|
79
79
|
this.edit.addEventListener("click", () => this.openEdit());
|
|
80
80
|
this.remove.addEventListener("click", () => this.apply(""));
|
|
81
81
|
this.input.addEventListener("keydown", (e) => {
|
|
82
|
+
if (e.isComposing || e.keyCode === 229) return;
|
|
82
83
|
if (e.key === "Enter") { e.preventDefault(); this.apply(this.input.value); }
|
|
83
84
|
else if (e.key === "Escape") { e.preventDefault(); this.close(); }
|
|
84
85
|
});
|
package/src/page/markdown.js
CHANGED
|
@@ -8,6 +8,7 @@ import { retype, replaceWithDivider } from "./items.js";
|
|
|
8
8
|
import { asStep } from "./blocksel.js";
|
|
9
9
|
import { insertTableNode } from "./table.js";
|
|
10
10
|
import { tableFromGrid } from "./table-paste.js";
|
|
11
|
+
import { safeHref } from "./address.js";
|
|
11
12
|
|
|
12
13
|
const { InputRule } = inputrules;
|
|
13
14
|
const N = schema.nodes;
|
|
@@ -29,6 +30,87 @@ const dividerRule = new InputRule(/^---$/, (st, match, start, end) => {
|
|
|
29
30
|
return asStep(replaceWithDivider(st.tr, $s.index(0)));
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
// Inline shortcuts typed around text: **bold** and __bold__ to strong, *italic*
|
|
34
|
+
// and _italic_ to em, `code`, ~~strike~~, and [label](address) to a link. The
|
|
35
|
+
// delimiters are deleted and the mark is added over the body, which stays where
|
|
36
|
+
// it is, so the marks it already carries survive. Backspace right after puts the
|
|
37
|
+
// delimiter characters back through the input rule's own undo.
|
|
38
|
+
const OPENS = "\\s\\(\\[\\{'\u2018\u201C\u0022";
|
|
39
|
+
const OPENS_ONE = new RegExp(`[${OPENS}]`);
|
|
40
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
41
|
+
const bodyRe = (exclude) => `([^\\s${exclude}](?:[\\s\\S]*?[^\\s${exclude}])?)`;
|
|
42
|
+
|
|
43
|
+
export const composing = (event, view) => !!event?.isComposing || event?.keyCode === 229 || !!view?.composing;
|
|
44
|
+
|
|
45
|
+
const trailingBackslashes = (text) => {
|
|
46
|
+
let n = 0;
|
|
47
|
+
for (let i = text.length - 1; i >= 0 && text[i] === "\\"; i--) n++;
|
|
48
|
+
return n;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// An opening delimiter needs the start of a textblock or whitespace and opening
|
|
52
|
+
// punctuation before it, and neither delimiter may be escaped.
|
|
53
|
+
const opensAt = (st, at) => {
|
|
54
|
+
const $at = st.doc.resolve(at);
|
|
55
|
+
if (!$at.parent.inlineContent) return false;
|
|
56
|
+
if (trailingBackslashes($at.parent.textBetween(0, $at.parentOffset, null, "\uFFFC")) % 2) return false;
|
|
57
|
+
return $at.parentOffset === 0 || OPENS_ONE.test($at.parent.textBetween($at.parentOffset - 1, $at.parentOffset));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const inline = (st, match, start, end, markOf) => {
|
|
61
|
+
const mark = markOf(match);
|
|
62
|
+
if (!mark || !st.selection.empty) return null;
|
|
63
|
+
if (start < 0 || start > end || end > st.doc.content.size) return null;
|
|
64
|
+
const $start = st.doc.resolve(start), $end = st.doc.resolve(end);
|
|
65
|
+
if (!$start.sameParent($end) || !$start.parent.inlineContent) return null;
|
|
66
|
+
const [all, prefix, body, close] = match;
|
|
67
|
+
if (inlineCode(st, start, end)) return null;
|
|
68
|
+
const open = all.slice(prefix.length, all.length - body.length - close.length);
|
|
69
|
+
const from = start + prefix.length;
|
|
70
|
+
if (!opensAt(st, from)) return null;
|
|
71
|
+
if (trailingBackslashes(all.slice(0, all.length - close.length)) % 2) return null;
|
|
72
|
+
const pending = all.length - (end - start);
|
|
73
|
+
if (pending < 0) return null;
|
|
74
|
+
const tr = st.tr;
|
|
75
|
+
if (pending) tr.insertText(all.slice(-pending), end, end);
|
|
76
|
+
const bodyStart = from + open.length;
|
|
77
|
+
const bodyEnd = bodyStart + body.length;
|
|
78
|
+
tr.delete(bodyEnd, start + all.length);
|
|
79
|
+
tr.delete(from, bodyStart);
|
|
80
|
+
tr.addMark(from, from + body.length, mark);
|
|
81
|
+
tr.removeStoredMark(mark.type);
|
|
82
|
+
return asStep(tr);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const inlineRule = (re, markOf) => new InputRule(
|
|
86
|
+
re,
|
|
87
|
+
(st, match, start, end) => inline(st, match, start, end, markOf),
|
|
88
|
+
{ inCodeMark: false },
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const markRule = (open, close, type) => inlineRule(
|
|
92
|
+
new RegExp(`(^|[${OPENS}])${escapeRe(open)}${bodyRe(escapeRe(close))}(${escapeRe(close)})$`),
|
|
93
|
+
() => type.create(),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const linkRule = inlineRule(
|
|
97
|
+
new RegExp(`(^|[${OPENS}])\\[${bodyRe("\\[\\]")}(\\]\\([^()\\s]*\\))$`),
|
|
98
|
+
(match) => {
|
|
99
|
+
const href = safeHref(match[3].slice(2, -1));
|
|
100
|
+
return href ? schema.marks.link.create({ href }) : null;
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const INLINE_RULES = [
|
|
105
|
+
markRule("**", "**", schema.marks.strong),
|
|
106
|
+
markRule("__", "__", schema.marks.strong),
|
|
107
|
+
markRule("*", "*", schema.marks.em),
|
|
108
|
+
markRule("_", "_", schema.marks.em),
|
|
109
|
+
markRule("`", "`", schema.marks.code),
|
|
110
|
+
markRule("~~", "~~", schema.marks.strike),
|
|
111
|
+
linkRule,
|
|
112
|
+
];
|
|
113
|
+
|
|
32
114
|
const LISTS = ["paragraph", "list_item"];
|
|
33
115
|
|
|
34
116
|
export const MARKDOWN_RULES = [
|
|
@@ -39,9 +121,34 @@ export const MARKDOWN_RULES = [
|
|
|
39
121
|
blockRule(/^>\s$/, { type: "toggle", attrs: { open: true } }),
|
|
40
122
|
blockRule(/^```$/, { type: "code_block" }, ["paragraph"], true),
|
|
41
123
|
dividerRule,
|
|
124
|
+
...INLINE_RULES,
|
|
42
125
|
];
|
|
43
126
|
|
|
44
|
-
|
|
127
|
+
// The shortest shortcut that quotes a line: `" ` typed at the start of a
|
|
128
|
+
// paragraph. It writes a floor-1 block, so it asks the format gate first and
|
|
129
|
+
// converts only when the gate answered applied; the transaction it dispatches is
|
|
130
|
+
// the input-rule one, recorded on this plugin so Backspace puts the literal `" `
|
|
131
|
+
// back and undo keeps the conversion whole. The typed space is never swallowed:
|
|
132
|
+
// when the format cannot be written, the space is inserted as itself.
|
|
133
|
+
function quoteShortcut(plugin, view, from, to, text) {
|
|
134
|
+
if (text !== " " || from !== to) return false;
|
|
135
|
+
const st = view.state;
|
|
136
|
+
const $from = st.doc.resolve(from);
|
|
137
|
+
const node = $from.parent;
|
|
138
|
+
if ($from.depth !== 1 || node.type !== N.paragraph || $from.parentOffset !== 1 || node.textContent !== '"') return false;
|
|
139
|
+
if (!view.editable || typeof view.runFormatCommand !== "function") return false;
|
|
140
|
+
const plain = () => { if (!view.isDestroyed) view.dispatch(st.tr.insertText(" ", from, to)); };
|
|
141
|
+
const at = $from.before(1);
|
|
142
|
+
import("./format-native-commands.js").then(({ quoteFromSpace }) => quoteFromSpace(plugin, view, { at, from, to }, plain), plain);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const markdownPlugin = () => {
|
|
147
|
+
const plugin = inputrules.inputRules({ rules: MARKDOWN_RULES });
|
|
148
|
+
const rules = plugin.props.handleTextInput;
|
|
149
|
+
plugin.props.handleTextInput = (view, from, to, text) => quoteShortcut(plugin, view, from, to, text) || rules(view, from, to, text);
|
|
150
|
+
return plugin;
|
|
151
|
+
};
|
|
45
152
|
|
|
46
153
|
// "| Name | Owner |" then Enter at the end of a paragraph: the paragraph becomes
|
|
47
154
|
// a table with those column names and one empty row, the caret in its first cell.
|
|
@@ -59,13 +166,15 @@ export function tableFromMarkdownRow(st, dispatch) {
|
|
|
59
166
|
return insertTableNode(st, dispatch, table, { from: $from.start(), to: $from.end() });
|
|
60
167
|
}
|
|
61
168
|
|
|
62
|
-
export const markdownKeys = () => keymap.keymap({
|
|
169
|
+
export const markdownKeys = () => keymap.keymap({
|
|
170
|
+
Enter: (st, dispatch, view) => !composing(null, view) && tableFromMarkdownRow(st, dispatch),
|
|
171
|
+
});
|
|
63
172
|
|
|
64
173
|
export function backspaceToParagraph(st, dispatch) {
|
|
65
174
|
const { $from, empty } = st.selection;
|
|
66
175
|
if (!empty || $from.depth !== 1 || $from.parentOffset !== 0) return false;
|
|
67
176
|
const n = $from.parent;
|
|
68
|
-
if (!(n.type === N.heading || n.type === N.callout || (n.type === N.code_block && n.content.size === 0))) return false;
|
|
177
|
+
if (!(n.type === N.heading || n.type === N.callout || n.type === N.quote || (n.type === N.code_block && n.content.size === 0))) return false;
|
|
69
178
|
if (dispatch) dispatch(asStep(st.tr.setNodeMarkup($from.before(), N.paragraph, { id: n.attrs.id, pos: n.attrs.pos, parent: n.attrs.parent })));
|
|
70
179
|
return true;
|
|
71
180
|
}
|