@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,162 @@
|
|
|
1
|
+
// The emoji a picker offers: a small curated set, grouped the way somebody looks for one.
|
|
2
|
+
// Each row is [glyph, name, keywords], and the glyph is the whole record: a variation
|
|
3
|
+
// selector, a skin tone, a flag's regional pair, a keycap or a joined family is written as
|
|
4
|
+
// the one grapheme a person sees, and nothing here is a font, a sprite, an image or a URL.
|
|
5
|
+
// It is fetched by `emoji.js` the first time the picker opens, never by a reader and never
|
|
6
|
+
// with the editor.
|
|
7
|
+
export const EMOJI = {
|
|
8
|
+
"Smileys": [
|
|
9
|
+
["😀", "grinning", "face smile happy"],
|
|
10
|
+
["😃", "grinning big eyes", "face smile happy"],
|
|
11
|
+
["😄", "grinning smiling eyes", "face laugh happy"],
|
|
12
|
+
["😁", "beaming", "face grin happy"],
|
|
13
|
+
["😆", "laughing", "face happy haha"],
|
|
14
|
+
["😅", "sweat smile", "face relief nervous"],
|
|
15
|
+
["😂", "joy", "face tears laugh lol"],
|
|
16
|
+
["🙂", "slight smile", "face ok"],
|
|
17
|
+
["🙃", "upside down", "face silly"],
|
|
18
|
+
["😉", "wink", "face flirt"],
|
|
19
|
+
["😊", "blush", "face smile warm"],
|
|
20
|
+
["😇", "halo", "face angel innocent"],
|
|
21
|
+
["🥰", "smiling hearts", "face love"],
|
|
22
|
+
["😍", "heart eyes", "face love crush"],
|
|
23
|
+
["🤩", "star struck", "face wow excited"],
|
|
24
|
+
["😎", "cool", "face sunglasses"],
|
|
25
|
+
["🤔", "thinking", "face hmm question"],
|
|
26
|
+
["😐", "neutral", "face flat"],
|
|
27
|
+
["😴", "sleeping", "face tired zzz"],
|
|
28
|
+
["😢", "crying", "face sad tear"],
|
|
29
|
+
["😭", "sobbing", "face sad tears"],
|
|
30
|
+
["😡", "angry", "face mad red"],
|
|
31
|
+
["😱", "scream", "face fear shock"],
|
|
32
|
+
["🤯", "exploding head", "face mind blown"],
|
|
33
|
+
["🥳", "party face", "celebration birthday"],
|
|
34
|
+
["😷", "mask", "face sick medical"],
|
|
35
|
+
["🤒", "thermometer", "face sick ill"],
|
|
36
|
+
],
|
|
37
|
+
"Gestures and people": [
|
|
38
|
+
["👋", "wave", "hand hello hi bye"],
|
|
39
|
+
["👋🏽", "wave medium skin tone", "hand hello hi"],
|
|
40
|
+
["👍", "thumbs up", "yes like approve"],
|
|
41
|
+
["👍🏿", "thumbs up dark skin tone", "yes like approve"],
|
|
42
|
+
["👎", "thumbs down", "no dislike"],
|
|
43
|
+
["👏", "clapping", "hands applause bravo"],
|
|
44
|
+
["🙌", "raised hands", "hooray praise"],
|
|
45
|
+
["🙏", "folded hands", "please thanks pray"],
|
|
46
|
+
["💪", "flexed arm", "strong muscle"],
|
|
47
|
+
["🤝", "handshake", "deal agreement"],
|
|
48
|
+
["👨👩👧👦", "family", "people children parents"],
|
|
49
|
+
["🧑💻", "technologist", "person laptop work code"],
|
|
50
|
+
["🏃", "runner", "person sport hurry"],
|
|
51
|
+
["🧘", "lotus", "person meditation yoga"],
|
|
52
|
+
],
|
|
53
|
+
"Animals and nature": [
|
|
54
|
+
["🐶", "dog", "animal pet puppy"],
|
|
55
|
+
["🐱", "cat", "animal pet kitten"],
|
|
56
|
+
["🐭", "mouse", "animal rodent"],
|
|
57
|
+
["🐰", "rabbit", "animal bunny"],
|
|
58
|
+
["🦊", "fox", "animal"],
|
|
59
|
+
["🐻", "bear", "animal"],
|
|
60
|
+
["🐼", "panda", "animal"],
|
|
61
|
+
["🐨", "koala", "animal"],
|
|
62
|
+
["🐯", "tiger", "animal"],
|
|
63
|
+
["🦁", "lion", "animal"],
|
|
64
|
+
["🐮", "cow", "animal"],
|
|
65
|
+
["🐷", "pig", "animal"],
|
|
66
|
+
["🐸", "frog", "animal"],
|
|
67
|
+
["🐵", "monkey", "animal"],
|
|
68
|
+
["🐔", "chicken", "animal bird"],
|
|
69
|
+
["🐧", "penguin", "animal bird"],
|
|
70
|
+
["🐦", "bird", "animal"],
|
|
71
|
+
["🦄", "unicorn", "animal magic"],
|
|
72
|
+
["🐝", "bee", "animal insect honey"],
|
|
73
|
+
["🦋", "butterfly", "animal insect"],
|
|
74
|
+
["🌸", "cherry blossom", "flower plant spring"],
|
|
75
|
+
["🌻", "sunflower", "flower plant summer"],
|
|
76
|
+
["🌈", "rainbow", "weather sky"],
|
|
77
|
+
["🌲", "evergreen tree", "plant forest"],
|
|
78
|
+
["🌵", "cactus", "plant desert"],
|
|
79
|
+
["🍁", "maple leaf", "plant autumn"],
|
|
80
|
+
],
|
|
81
|
+
"Food and drink": [
|
|
82
|
+
["☕", "coffee", "drink cup hot"],
|
|
83
|
+
["🍵", "tea", "drink cup green"],
|
|
84
|
+
["🍺", "beer", "drink mug"],
|
|
85
|
+
["🍷", "wine", "drink glass"],
|
|
86
|
+
["🍎", "apple", "fruit food red"],
|
|
87
|
+
["🍌", "banana", "fruit food"],
|
|
88
|
+
["🍇", "grapes", "fruit food"],
|
|
89
|
+
["🍓", "strawberry", "fruit food"],
|
|
90
|
+
["🍑", "peach", "fruit food"],
|
|
91
|
+
["🍕", "pizza", "food slice"],
|
|
92
|
+
["🍔", "burger", "food"],
|
|
93
|
+
["🍟", "fries", "food"],
|
|
94
|
+
["🌮", "taco", "food"],
|
|
95
|
+
["🍣", "sushi", "food fish"],
|
|
96
|
+
["🍜", "noodles", "food ramen"],
|
|
97
|
+
["🍰", "cake", "food dessert"],
|
|
98
|
+
["🍪", "cookie", "food sweet"],
|
|
99
|
+
["🍫", "chocolate", "food sweet"],
|
|
100
|
+
["🥑", "avocado", "food fruit"],
|
|
101
|
+
["🥕", "carrot", "food vegetable"],
|
|
102
|
+
["🍞", "bread", "food loaf"],
|
|
103
|
+
],
|
|
104
|
+
"Travel and places": [
|
|
105
|
+
["✈️", "airplane", "travel flight"],
|
|
106
|
+
["🚗", "car", "travel drive"],
|
|
107
|
+
["🚲", "bicycle", "travel bike"],
|
|
108
|
+
["🚂", "train", "travel locomotive"],
|
|
109
|
+
["🚀", "rocket", "travel launch space"],
|
|
110
|
+
["⛵", "sailboat", "travel boat"],
|
|
111
|
+
["🏠", "house", "place home"],
|
|
112
|
+
["🏢", "office", "place building work"],
|
|
113
|
+
["🗽", "statue of liberty", "place new york"],
|
|
114
|
+
["🗼", "tower", "place tokyo"],
|
|
115
|
+
["🏔", "mountain", "place snow"],
|
|
116
|
+
["🏖", "beach", "place holiday sea"],
|
|
117
|
+
["🌍", "globe", "world earth"],
|
|
118
|
+
["🗺", "map", "place world"],
|
|
119
|
+
["🚦", "traffic light", "place street"],
|
|
120
|
+
],
|
|
121
|
+
"Objects": [
|
|
122
|
+
["💡", "bulb", "idea light"],
|
|
123
|
+
["🔥", "fire", "hot flame"],
|
|
124
|
+
["⭐", "star", "favourite rating"],
|
|
125
|
+
["💯", "hundred", "score perfect"],
|
|
126
|
+
["✅", "check", "done yes"],
|
|
127
|
+
["❌", "cross", "no wrong"],
|
|
128
|
+
["⚠️", "warning", "caution alert"],
|
|
129
|
+
["📌", "pushpin", "pin note"],
|
|
130
|
+
["📎", "paperclip", "attach"],
|
|
131
|
+
["📅", "calendar", "date schedule"],
|
|
132
|
+
["📈", "chart up", "graph growth"],
|
|
133
|
+
["📉", "chart down", "graph loss"],
|
|
134
|
+
["💰", "money bag", "cash"],
|
|
135
|
+
["🔑", "key", "access"],
|
|
136
|
+
["🔒", "lock", "secure private"],
|
|
137
|
+
["🔔", "bell", "notify alert"],
|
|
138
|
+
["🎉", "party popper", "celebrate"],
|
|
139
|
+
["🎁", "gift", "present"],
|
|
140
|
+
["🏆", "trophy", "win award"],
|
|
141
|
+
["🎯", "target", "goal aim"],
|
|
142
|
+
["⏰", "alarm clock", "time"],
|
|
143
|
+
["⌛", "hourglass", "time wait"],
|
|
144
|
+
],
|
|
145
|
+
"Symbols": [
|
|
146
|
+
["❤️", "red heart", "love"],
|
|
147
|
+
["💔", "broken heart", "love sad"],
|
|
148
|
+
["✨", "sparkles", "magic new"],
|
|
149
|
+
["1️⃣", "keycap one", "number"],
|
|
150
|
+
["2️⃣", "keycap two", "number"],
|
|
151
|
+
["#️⃣", "keycap hash", "number channel"],
|
|
152
|
+
["🇫🇷", "flag france", "country"],
|
|
153
|
+
["🇺🇸", "flag united states", "country"],
|
|
154
|
+
["🇯🇵", "flag japan", "country"],
|
|
155
|
+
["♻️", "recycle", "reuse"],
|
|
156
|
+
["➡️", "arrow right", "direction next"],
|
|
157
|
+
["⬅️", "arrow left", "direction back"],
|
|
158
|
+
["🔁", "repeat", "loop again"],
|
|
159
|
+
["🔀", "shuffle", "random"],
|
|
160
|
+
["🆗", "ok button", "yes"],
|
|
161
|
+
],
|
|
162
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Optional editor features load the first time a document needs them. One import per
|
|
2
|
+
// feature; a caller's abort stops only its own wait; dispose() makes late results inert.
|
|
3
|
+
export function createFeatureLoader({ load, install, reportError = (error) => console.error("clayjs: an editor feature failed to load", error) }) {
|
|
4
|
+
const installed = new Map();
|
|
5
|
+
const pending = new Map();
|
|
6
|
+
const prepared = new Map();
|
|
7
|
+
let disposed = false;
|
|
8
|
+
|
|
9
|
+
// A preparation fetches the module without installing it, so the boot can overlap
|
|
10
|
+
// that fetch with the room join. The install that follows consumes this same
|
|
11
|
+
// import: a feature is fetched once, and a failed fetch is that attempt. The next
|
|
12
|
+
// ensure after it loads again through the loader's reporting and the feature's
|
|
13
|
+
// Retry.
|
|
14
|
+
function prepare(id) {
|
|
15
|
+
if (disposed) return Promise.reject(new Error(`page: feature loader disposed before ${id}`));
|
|
16
|
+
let shared = prepared.get(id);
|
|
17
|
+
if (!shared) {
|
|
18
|
+
shared = Promise.resolve(load(id));
|
|
19
|
+
prepared.set(id, shared);
|
|
20
|
+
// The install reports the failure; this keeps an unconsumed preparation quiet.
|
|
21
|
+
shared.catch(() => {});
|
|
22
|
+
}
|
|
23
|
+
return shared;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function loadFor(id) {
|
|
27
|
+
const fetched = prepared.get(id);
|
|
28
|
+
if (!fetched) return load(id);
|
|
29
|
+
prepared.delete(id);
|
|
30
|
+
return fetched;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function ensure(id, { signal } = {}) {
|
|
34
|
+
if (disposed) return Promise.reject(new Error(`page: feature loader disposed before ${id}`));
|
|
35
|
+
if (installed.has(id)) return Promise.resolve(installed.get(id));
|
|
36
|
+
let shared = pending.get(id);
|
|
37
|
+
if (!shared) {
|
|
38
|
+
shared = loadFor(id)
|
|
39
|
+
.then((mod) => (disposed ? null : Promise.resolve(install(id, mod))))
|
|
40
|
+
.then((feature) => {
|
|
41
|
+
if (disposed) {
|
|
42
|
+
feature?.dispose?.();
|
|
43
|
+
return feature;
|
|
44
|
+
}
|
|
45
|
+
installed.set(id, feature);
|
|
46
|
+
return feature;
|
|
47
|
+
});
|
|
48
|
+
shared.catch((error) => { if (!disposed) reportError(error, id); }).finally(() => pending.delete(id));
|
|
49
|
+
pending.set(id, shared);
|
|
50
|
+
}
|
|
51
|
+
if (!signal) return shared;
|
|
52
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const onAbort = () => reject(signal.reason);
|
|
55
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
56
|
+
shared.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
prepare,
|
|
62
|
+
ensure,
|
|
63
|
+
has: (id) => installed.has(id),
|
|
64
|
+
dispose() {
|
|
65
|
+
disposed = true;
|
|
66
|
+
for (const feature of installed.values()) feature?.dispose?.();
|
|
67
|
+
installed.clear();
|
|
68
|
+
pending.clear();
|
|
69
|
+
prepared.clear();
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BOARD_CSS } from "../board-style.js";
|
|
2
|
+
import { BoardView } from "../board-view.js";
|
|
3
|
+
import { cardPanel } from "../board-panel.js";
|
|
4
|
+
import { cardPanels } from "../board-views.js";
|
|
5
|
+
import { addViewHook } from "../view-hooks.js";
|
|
6
|
+
|
|
7
|
+
export function createFeature({ view = null, board = null, undo = null, redo = null } = {}) {
|
|
8
|
+
const off = view ? addViewHook(view, {
|
|
9
|
+
update: (editorView) => cardPanels.get(editorView)?.sync(),
|
|
10
|
+
destroy: (editorView) => {
|
|
11
|
+
cardPanels.get(editorView)?.hide(false, false);
|
|
12
|
+
cardPanels.delete(editorView);
|
|
13
|
+
},
|
|
14
|
+
}) : () => {};
|
|
15
|
+
return {
|
|
16
|
+
id: "board",
|
|
17
|
+
nodeViews: board ? { board: (node, editorView, getPos) => new BoardView(node, editorView, getPos, { commands: board, undo, redo, panel: cardPanel(editorView) }) } : null,
|
|
18
|
+
css: board ? BOARD_CSS : null,
|
|
19
|
+
dispose() { off(); },
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ChartView, refreshCharts } from "../chart.js";
|
|
2
|
+
import { addViewHook } from "../view-hooks.js";
|
|
3
|
+
|
|
4
|
+
export function createFeature({ view = null } = {}) {
|
|
5
|
+
const off = view ? addViewHook(view, { update: refreshCharts }) : () => {};
|
|
6
|
+
return {
|
|
7
|
+
id: "chart",
|
|
8
|
+
nodeViews: { chart: (node, editorView, getPos) => new ChartView(node, editorView, getPos) },
|
|
9
|
+
dispose() { off(); },
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Code blocks: their language picker and the syntax colours. Loaded the first
|
|
2
|
+
// time a document has or takes a code block.
|
|
3
|
+
import { CodeBlockView, startHighlighter } from "../highlight.js";
|
|
4
|
+
|
|
5
|
+
export function createFeature({ view = null, highlight = {} } = {}) {
|
|
6
|
+
const highlighter = view ? startHighlighter(view, highlight) : null;
|
|
7
|
+
return {
|
|
8
|
+
id: "code",
|
|
9
|
+
nodeViews: { code_block: (node, v, getPos) => new CodeBlockView(node, v, getPos) },
|
|
10
|
+
css: null,
|
|
11
|
+
dispose() { highlighter?.destroy(); },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Date mentions: the Date action offers Today and Tomorrow, and choosing one writes that
|
|
2
|
+
// calendar day into the document as ordinary ISO text with the `date` mark over it. Both
|
|
3
|
+
// days are resolved once, when the command runs, from the local calendar rather than from a
|
|
4
|
+
// UTC instant, so the text a person reads is the day they picked; nothing here stores a
|
|
5
|
+
// time, a zone, a relative word or a reminder, and nothing speaks to the network. Loaded on
|
|
6
|
+
// the first Date action, and disposed with the exit.
|
|
7
|
+
import { state } from "../../vendor/prosemirror.vendor.js";
|
|
8
|
+
import { schema } from "../schema.js";
|
|
9
|
+
import { asStep } from "../blocksel.js";
|
|
10
|
+
import { composing } from "../markdown.js";
|
|
11
|
+
import { createMentionPicker } from "../mention-picker.js";
|
|
12
|
+
import { runFormatted } from "../format-command.js";
|
|
13
|
+
|
|
14
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
15
|
+
|
|
16
|
+
// A calendar date, as the record stores it: no year padding games, no leap second, no
|
|
17
|
+
// zone. A day that does not exist is not a date, so 2023-02-29 is refused.
|
|
18
|
+
export function isISODate(value) {
|
|
19
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
|
|
20
|
+
const parsed = new Date(`${value}T12:00:00Z`);
|
|
21
|
+
return Number.isFinite(parsed.valueOf()) && parsed.toISOString().slice(0, 10) === value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Today, from the local calendar: the fields a person reads on their own wall clock, never
|
|
25
|
+
// `toISOString()` of a local time, which is tomorrow or yesterday for part of the day.
|
|
26
|
+
export const localISODate = (now = new Date()) => `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
|
27
|
+
|
|
28
|
+
// A day after (or before) a date, by calendar arithmetic on the date alone: noon UTC keeps
|
|
29
|
+
// a daylight-saving shift from landing on the day before.
|
|
30
|
+
const dayAfter = (date, days) => {
|
|
31
|
+
const moved = new Date(`${date}T12:00:00Z`);
|
|
32
|
+
moved.setUTCDate(moved.getUTCDate() + days);
|
|
33
|
+
return moved.toISOString().slice(0, 10);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// What the picker lists: two days, both resolved here and once, labelled with the text each
|
|
37
|
+
// one writes. Nothing is stored and no day is invented later.
|
|
38
|
+
export const earlyDays = (today) => {
|
|
39
|
+
const tomorrow = dayAfter(today, 1);
|
|
40
|
+
return [
|
|
41
|
+
{ id: "today", name: `Today (${today})`, date: today },
|
|
42
|
+
{ id: "tomorrow", name: `Tomorrow (${tomorrow})`, date: tomorrow },
|
|
43
|
+
];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function createFeature({ view = null } = {}) {
|
|
47
|
+
if (!view) return { run: () => ({ ok: false, code: "not-editing" }), dispose() {} };
|
|
48
|
+
const doc = view.dom.ownerDocument;
|
|
49
|
+
let session = null;
|
|
50
|
+
|
|
51
|
+
// The caret the command belongs to, read from the document again at the commit: the
|
|
52
|
+
// picker holds no focus and writes nothing, so a caret that moved, a selection that is no
|
|
53
|
+
// longer empty or a block that refuses marks all mean the bookmark no longer holds and the
|
|
54
|
+
// command refuses rather than writing the date somewhere else.
|
|
55
|
+
function bookmark(s) {
|
|
56
|
+
const st = view.state;
|
|
57
|
+
if (!st.selection.empty || st.selection.from !== s.from) return null;
|
|
58
|
+
if (s.from < 0 || s.from > st.doc.content.size) return null;
|
|
59
|
+
const $from = st.doc.resolve(s.from);
|
|
60
|
+
if (!$from.parent.isTextblock || $from.parent.type.spec.code) return null;
|
|
61
|
+
if (!$from.parent.type.allowsMarkType(schema.marks.date)) return null;
|
|
62
|
+
return s.from;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Where the list hangs, in the page's own placement variables. A view that cannot measure
|
|
66
|
+
// the caret (one outside a laid-out document) still gets its list, just not positioned.
|
|
67
|
+
function place(s) {
|
|
68
|
+
try {
|
|
69
|
+
const box = view.dom.parentElement?.getBoundingClientRect?.() ?? { left: 0, top: 0 };
|
|
70
|
+
const at = view.coordsAtPos(s.from);
|
|
71
|
+
return { x: Math.max(8, at.left - box.left), y: at.bottom + 6 - box.top };
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function anchor(s) {
|
|
78
|
+
const id = s.picker.activeId();
|
|
79
|
+
if (id) view.dom.setAttribute("aria-activedescendant", id);
|
|
80
|
+
else if (view.dom.getAttribute("aria-activedescendant")?.startsWith(s.picker.el.id)) view.dom.removeAttribute("aria-activedescendant");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function close() {
|
|
84
|
+
const s = session;
|
|
85
|
+
if (!s) return;
|
|
86
|
+
session = null;
|
|
87
|
+
for (const [target, type, fn, options] of s.listen) target.removeEventListener(type, fn, options);
|
|
88
|
+
if (view.dom.getAttribute("aria-activedescendant")?.startsWith(`${s.picker.el.id}-`)) view.dom.removeAttribute("aria-activedescendant");
|
|
89
|
+
s.picker.destroy();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The one place a date reaches the document. Document 14 Step 8a wraps this call in the
|
|
93
|
+
// format gate's `runFormatCommand`; nothing else in this module writes.
|
|
94
|
+
function insertDateMention(at, date) {
|
|
95
|
+
const label = String(date);
|
|
96
|
+
// One transaction: the ISO text and its mark, then one unmarked space so typing at the
|
|
97
|
+
// edge of a date stays plain text.
|
|
98
|
+
const tr = view.state.tr.insertText(label, at, at);
|
|
99
|
+
tr.addMark(at, at + label.length, schema.marks.date.create({ date: label }));
|
|
100
|
+
tr.insertText(" ", at + label.length);
|
|
101
|
+
tr.setSelection(state.TextSelection.create(tr.doc, at + label.length + 1));
|
|
102
|
+
view.dispatch(asStep(tr));
|
|
103
|
+
view.focus();
|
|
104
|
+
return { ok: true, date: label };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function choose(index) {
|
|
108
|
+
const s = session;
|
|
109
|
+
if (!s) return { ok: false, code: "not-editing" };
|
|
110
|
+
const entry = s.picker.personAt(index);
|
|
111
|
+
const at = bookmark(s);
|
|
112
|
+
if (at === null || !entry || !isISODate(entry.date)) {
|
|
113
|
+
close();
|
|
114
|
+
if (!entry) return { ok: false, code: "no-date" };
|
|
115
|
+
if (at === null) return { ok: false, code: "stale-target" };
|
|
116
|
+
return { ok: false, code: "invalid-date" };
|
|
117
|
+
}
|
|
118
|
+
close();
|
|
119
|
+
// The gate awaits, so the caret is read again here: one that moved, a selection that is
|
|
120
|
+
// no longer a caret or a block that refuses marks means the target no longer holds, and
|
|
121
|
+
// the date is not written anywhere.
|
|
122
|
+
return runFormatted(view, () => {
|
|
123
|
+
const now = bookmark(s);
|
|
124
|
+
if (now === null) return { ok: false, code: "stale-target" };
|
|
125
|
+
return insertDateMention(now, entry.date);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function key(event) {
|
|
130
|
+
const s = session;
|
|
131
|
+
if (!s || composing(event, view)) return;
|
|
132
|
+
const stop = () => { event.preventDefault(); event.stopPropagation(); };
|
|
133
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
134
|
+
stop();
|
|
135
|
+
s.picker.move(event.key === "ArrowDown" ? 1 : -1);
|
|
136
|
+
anchor(s);
|
|
137
|
+
} else if (event.key === "Enter") {
|
|
138
|
+
stop();
|
|
139
|
+
choose(s.picker.index());
|
|
140
|
+
} else if (event.key === "Escape" || event.key === "Tab") {
|
|
141
|
+
stop();
|
|
142
|
+
close();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function open() {
|
|
147
|
+
close();
|
|
148
|
+
// Today is read once, here: a picker left open across midnight still writes the day the
|
|
149
|
+
// command was asked for, and choosing Tomorrow never reads the clock again.
|
|
150
|
+
const s = { from: view.state.selection.from, days: earlyDays(localISODate()), picker: null, listen: [] };
|
|
151
|
+
s.picker = createMentionPicker({ document: doc, label: "Insert a date", empty: "No date to insert", onPick: (index) => choose(index) });
|
|
152
|
+
session = s;
|
|
153
|
+
const listen = (target, type, fn, options) => {
|
|
154
|
+
target.addEventListener(type, fn, options);
|
|
155
|
+
s.listen.push([target, type, fn, options]);
|
|
156
|
+
};
|
|
157
|
+
listen(doc, "keydown", (event) => key(event), true);
|
|
158
|
+
// Typing closes the list: the caret has moved on, and the command that opened it has
|
|
159
|
+
// nothing to write where it no longer holds.
|
|
160
|
+
listen(view.dom, "input", () => close());
|
|
161
|
+
listen(doc, "mousedown", (event) => { if (!s.picker.el.contains(event.target)) close(); }, true);
|
|
162
|
+
(view.dom.parentElement ?? doc.body).appendChild(s.picker.el);
|
|
163
|
+
if (bookmark(s) === null) {
|
|
164
|
+
close();
|
|
165
|
+
return { ok: false, code: "stale-target" };
|
|
166
|
+
}
|
|
167
|
+
s.picker.show(s.days, place(s));
|
|
168
|
+
anchor(s);
|
|
169
|
+
return { ok: true, picker: true };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
// The Date action: the picker at the caret, offering the two days resolved now. It
|
|
174
|
+
// writes nothing until one is chosen.
|
|
175
|
+
async run() {
|
|
176
|
+
return open();
|
|
177
|
+
},
|
|
178
|
+
dispose() { close(); },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Emoji: the Emoji action opens a picker of a small bundled glyph list and choosing one
|
|
2
|
+
// writes that glyph into the document as ordinary Unicode text. The glyph is the whole
|
|
3
|
+
// record — no mark, no node, no attribute and no format floor — so a reader shows it with
|
|
4
|
+
// nothing of this loaded, and a variation selector, a skin tone, a flag's regional pair, a
|
|
5
|
+
// keycap or a joined family goes in as the one grapheme a person sees, never split. The
|
|
6
|
+
// list is fetched the first time the picker opens and kept; a fetch that failed is not
|
|
7
|
+
// kept, so the next opening asks again.
|
|
8
|
+
import { state } from "../../vendor/prosemirror.vendor.js";
|
|
9
|
+
import { asStep } from "../blocksel.js";
|
|
10
|
+
import { composing } from "../markdown.js";
|
|
11
|
+
import { createMentionPicker } from "../mention-picker.js";
|
|
12
|
+
|
|
13
|
+
// One glyph, as a person sees it: counting code points or UTF-16 units would call a
|
|
14
|
+
// joined family seven things and a keycap several, and cut one in half.
|
|
15
|
+
export function graphemes(text) {
|
|
16
|
+
const value = String(text ?? "");
|
|
17
|
+
const segmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function" ? new Intl.Segmenter("en", { granularity: "grapheme" }) : null;
|
|
18
|
+
return segmenter ? [...segmenter.segment(value)].map((part) => part.segment) : [...value];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const isEmojiText = (value) => graphemes(value).length === 1 && !/^\s*$/.test(String(value ?? ""));
|
|
22
|
+
|
|
23
|
+
// The data module's groups, flattened into the rows a picker lists: the glyph first, so the
|
|
24
|
+
// row reads as the emoji and its name, and the searchable words after it.
|
|
25
|
+
export function emojiEntries(groups) {
|
|
26
|
+
const list = [];
|
|
27
|
+
for (const [group, rows] of Object.entries(groups ?? {})) {
|
|
28
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
29
|
+
const [char, name, keywords = ""] = row ?? [];
|
|
30
|
+
if (typeof char !== "string" || typeof name !== "string") continue;
|
|
31
|
+
list.push({ id: `${group}:${name}`, char, name: `${char} ${name}`, keywords: `${name} ${keywords}` });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return list;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const filterEmoji = (list, query) => {
|
|
38
|
+
const q = String(query ?? "").trim().toLowerCase();
|
|
39
|
+
if (!q) return list;
|
|
40
|
+
return list.filter((entry) => entry.char === q || entry.keywords.toLowerCase().includes(q));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// The list as one fetch for the life of a session: the first opening loads it, the next
|
|
44
|
+
// openings reuse it, and a fetch that failed leaves nothing cached so it is asked for again.
|
|
45
|
+
export function emojiLibrary(load = () => import("../emoji-data.js")) {
|
|
46
|
+
let pending = null;
|
|
47
|
+
return () => {
|
|
48
|
+
pending ??= Promise.resolve()
|
|
49
|
+
.then(() => load())
|
|
50
|
+
.then((mod) => emojiEntries(mod.EMOJI))
|
|
51
|
+
.catch((error) => { pending = null; throw error; });
|
|
52
|
+
return pending;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createFeature({ view = null, load = () => import("../emoji-data.js") } = {}) {
|
|
57
|
+
if (!view) return { run: () => ({ ok: false, code: "not-editing" }), dispose() {} };
|
|
58
|
+
const doc = view.dom.ownerDocument;
|
|
59
|
+
const library = emojiLibrary(load);
|
|
60
|
+
let session = null;
|
|
61
|
+
|
|
62
|
+
// The text the command belongs to, read from the document again at the commit: the picker
|
|
63
|
+
// holds no focus and writes nothing, so a caret that moved, a selection that is no longer
|
|
64
|
+
// the one the command was asked for or a place that takes no text all mean the bookmark no
|
|
65
|
+
// longer holds, and the command refuses rather than writing the glyph somewhere else.
|
|
66
|
+
function bookmark(s) {
|
|
67
|
+
const st = view.state;
|
|
68
|
+
const sel = st.selection;
|
|
69
|
+
if (sel.from !== s.from || sel.to !== s.to) return null;
|
|
70
|
+
if (s.from < 0 || s.to > st.doc.content.size) return null;
|
|
71
|
+
const $from = st.doc.resolve(s.from);
|
|
72
|
+
const $to = st.doc.resolve(s.to);
|
|
73
|
+
if (!$from.parent.isTextblock || $from.parent !== $to.parent) return null;
|
|
74
|
+
return { from: s.from, to: s.to };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Where the list hangs, in the page's own placement variables. A view that cannot measure
|
|
78
|
+
// the caret (one outside a laid-out document) still gets its list, just not positioned.
|
|
79
|
+
function place(s) {
|
|
80
|
+
try {
|
|
81
|
+
const box = view.dom.parentElement?.getBoundingClientRect?.() ?? { left: 0, top: 0 };
|
|
82
|
+
const at = view.coordsAtPos(s.from);
|
|
83
|
+
return { x: Math.max(8, at.left - box.left), y: at.bottom + 6 - box.top };
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function anchor(s) {
|
|
90
|
+
const id = s.picker.activeId();
|
|
91
|
+
if (id) view.dom.setAttribute("aria-activedescendant", id);
|
|
92
|
+
else if (view.dom.getAttribute("aria-activedescendant")?.startsWith(s.picker.el.id)) view.dom.removeAttribute("aria-activedescendant");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function close() {
|
|
96
|
+
const s = session;
|
|
97
|
+
if (!s) return;
|
|
98
|
+
session = null;
|
|
99
|
+
for (const [target, type, fn, options] of s.listen) target.removeEventListener(type, fn, options);
|
|
100
|
+
if (view.dom.getAttribute("aria-activedescendant")?.startsWith(`${s.picker.el.id}-`)) view.dom.removeAttribute("aria-activedescendant");
|
|
101
|
+
s.picker.destroy();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The one place a glyph reaches the document: the selection's text is replaced by it in a
|
|
105
|
+
// single transaction, with no mark and nothing else added.
|
|
106
|
+
function insertEmoji(at, char) {
|
|
107
|
+
const tr = view.state.tr.insertText(char, at.from, at.to);
|
|
108
|
+
tr.setSelection(state.TextSelection.create(tr.doc, at.from + char.length));
|
|
109
|
+
view.dispatch(asStep(tr));
|
|
110
|
+
view.focus();
|
|
111
|
+
return { ok: true, emoji: char };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function choose(index) {
|
|
115
|
+
const s = session;
|
|
116
|
+
if (!s) return { ok: false, code: "not-editing" };
|
|
117
|
+
const entry = s.picker.personAt(index);
|
|
118
|
+
const at = bookmark(s);
|
|
119
|
+
if (!entry || at === null || !isEmojiText(entry.char)) {
|
|
120
|
+
close();
|
|
121
|
+
if (!entry) return { ok: false, code: "no-emoji" };
|
|
122
|
+
if (at === null) return { ok: false, code: "stale-target" };
|
|
123
|
+
return { ok: false, code: "invalid-emoji" };
|
|
124
|
+
}
|
|
125
|
+
close();
|
|
126
|
+
return insertEmoji(at, entry.char);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function key(event) {
|
|
130
|
+
const s = session;
|
|
131
|
+
if (!s || composing(event, view)) return;
|
|
132
|
+
const stop = () => { event.preventDefault(); event.stopPropagation(); };
|
|
133
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
134
|
+
stop();
|
|
135
|
+
s.picker.move(event.key === "ArrowDown" ? 1 : -1);
|
|
136
|
+
anchor(s);
|
|
137
|
+
} else if (event.key === "Enter") {
|
|
138
|
+
stop();
|
|
139
|
+
choose(s.picker.index());
|
|
140
|
+
} else if (event.key === "Escape" || event.key === "Tab") {
|
|
141
|
+
stop();
|
|
142
|
+
close();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function open(args = {}) {
|
|
147
|
+
close();
|
|
148
|
+
const s = { from: view.state.selection.from, to: view.state.selection.to, picker: null, listen: [] };
|
|
149
|
+
s.picker = createMentionPicker({ document: doc, label: "Insert an emoji", empty: "No emoji found", onPick: (index) => choose(index) });
|
|
150
|
+
session = s;
|
|
151
|
+
const listen = (target, type, fn, options) => {
|
|
152
|
+
target.addEventListener(type, fn, options);
|
|
153
|
+
s.listen.push([target, type, fn, options]);
|
|
154
|
+
};
|
|
155
|
+
listen(doc, "keydown", (event) => key(event), true);
|
|
156
|
+
// Typing closes the list: the caret has moved on, and the command that opened it has
|
|
157
|
+
// nothing to write where it no longer holds.
|
|
158
|
+
listen(view.dom, "input", () => close());
|
|
159
|
+
listen(doc, "mousedown", (event) => { if (!s.picker.el.contains(event.target)) close(); }, true);
|
|
160
|
+
(view.dom.parentElement ?? doc.body).appendChild(s.picker.el);
|
|
161
|
+
|
|
162
|
+
let list;
|
|
163
|
+
try {
|
|
164
|
+
list = await library();
|
|
165
|
+
} catch (error) {
|
|
166
|
+
close();
|
|
167
|
+
return { ok: false, code: "unavailable", error };
|
|
168
|
+
}
|
|
169
|
+
if (session !== s) return { ok: false, code: "not-editing" };
|
|
170
|
+
if (bookmark(s) === null) {
|
|
171
|
+
close();
|
|
172
|
+
return { ok: false, code: "stale-target" };
|
|
173
|
+
}
|
|
174
|
+
s.picker.show(filterEmoji(list, args?.query), place(s));
|
|
175
|
+
anchor(s);
|
|
176
|
+
return { ok: true, picker: true };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
// The Emoji action: the picker at the caret, offering the bundled list. It writes
|
|
181
|
+
// nothing until one is chosen.
|
|
182
|
+
async run(args) {
|
|
183
|
+
return open(args);
|
|
184
|
+
},
|
|
185
|
+
dispose() { close(); },
|
|
186
|
+
};
|
|
187
|
+
}
|