@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,230 @@
|
|
|
1
|
+
// Other people's carets on the page.
|
|
2
|
+
//
|
|
3
|
+
// A peer publishes where its selection sits (Document 10 Step 6): `presence`
|
|
4
|
+
// carries a page target, and this module draws it where that text is in *this*
|
|
5
|
+
// tab's document, through the binding's mapping. Nothing here is saved: the
|
|
6
|
+
// decorations are ProseMirror's, the labels are runtime elements, and the only
|
|
7
|
+
// thing this file sends is a target the presence store already coalesces.
|
|
8
|
+
//
|
|
9
|
+
// It loads on its own, after presence is available in an editor, so a reader and
|
|
10
|
+
// an editor that nobody shares with never fetch it.
|
|
11
|
+
import { state, view as pmView } from "../vendor/prosemirror.vendor.js";
|
|
12
|
+
import { encodeSelection, decodeSelection } from "./ybind/awareness.js";
|
|
13
|
+
import { presence as presenceStore } from "../sync/presence-store.js";
|
|
14
|
+
import { PRESENCE_VERSION } from "../sync/presence-target.js";
|
|
15
|
+
import { hostMeta } from "../core/host-meta.js";
|
|
16
|
+
import { styleWithLegacyId } from "../lib/style.js";
|
|
17
|
+
|
|
18
|
+
const { Plugin, PluginKey } = state;
|
|
19
|
+
const { Decoration, DecorationSet } = pmView;
|
|
20
|
+
|
|
21
|
+
export const awarenessKey = new PluginKey("page-awareness");
|
|
22
|
+
|
|
23
|
+
const STYLE_ID = "page-awareness-style";
|
|
24
|
+
const STYLE_CSS = `
|
|
25
|
+
.page-remote-selection { background-color: color-mix(in srgb, var(--page-remote-color, currentColor) 22%, transparent); }
|
|
26
|
+
.page-remote-caret { position: relative; display: inline-block; width: 0; height: 1.1em; vertical-align: text-bottom; }
|
|
27
|
+
.page-remote-caret::before { content: ""; position: absolute; inset-inline-start: -1px; top: 0; bottom: 0; width: 2px; background: var(--page-remote-color, currentColor); }
|
|
28
|
+
.page-remote-name { position: absolute; inset-inline-start: -1px; bottom: 100%; padding: 1px 4px; border-radius: 4px; background: var(--page-remote-color, currentColor); color: #fff; font-size: 10px; line-height: 1.4; white-space: nowrap; pointer-events: none; }
|
|
29
|
+
.page-remote-cell { outline: 2px solid var(--page-remote-color, currentColor); outline-offset: -2px; }
|
|
30
|
+
.page-remote-block { box-shadow: inset 2px 0 0 var(--page-remote-color, currentColor); }
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
// A fixed palette, chosen for contrast against both page themes. A peer's id
|
|
34
|
+
// picks the colour; a peer never supplies one.
|
|
35
|
+
const PALETTE = ["#2F5E9E", "#8A3B6E", "#3E6A28", "#8A5A1E", "#5E4596", "#1F5A8A"];
|
|
36
|
+
export const colorOf = (id) => {
|
|
37
|
+
const text = typeof id === "string" ? id : "";
|
|
38
|
+
let hash = 0;
|
|
39
|
+
for (let i = 0; i < text.length; i++) hash = (hash * 31 + text.charCodeAt(i)) >>> 0;
|
|
40
|
+
return PALETTE[hash % PALETTE.length];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const caretElement = (doc, name, color) => {
|
|
44
|
+
const bar = doc.createElement("span");
|
|
45
|
+
bar.className = "page-remote-caret";
|
|
46
|
+
bar.setAttribute("aria-hidden", "true");
|
|
47
|
+
bar.style.setProperty("--page-remote-color", color);
|
|
48
|
+
const tag = doc.createElement("span");
|
|
49
|
+
tag.className = "page-remote-name";
|
|
50
|
+
tag.textContent = name;
|
|
51
|
+
bar.appendChild(tag);
|
|
52
|
+
return bar;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const labelOf = (person) => (typeof person.name === "string" && person.name.trim() ? person.name.trim() : "Someone");
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The decorations one snapshot produces in one document.
|
|
59
|
+
*
|
|
60
|
+
* `decode` returns null for anything this tab cannot place — a hidden copy, a
|
|
61
|
+
* deleted block, a peer's relative position from another document, bytes that do
|
|
62
|
+
* not decode — and a null is a caret that simply is not drawn.
|
|
63
|
+
*/
|
|
64
|
+
export function decorationsFor({ binding, doc, snapshot, epoch }) {
|
|
65
|
+
const generation = binding.awareness.generation();
|
|
66
|
+
const context = { ...binding.awareness.map(), doc };
|
|
67
|
+
const decorations = [];
|
|
68
|
+
for (const person of snapshot.people ?? []) {
|
|
69
|
+
if (person.you) continue;
|
|
70
|
+
const color = colorOf(person.id);
|
|
71
|
+
const name = labelOf(person);
|
|
72
|
+
for (const session of person.sessions ?? []) {
|
|
73
|
+
if (session.you) continue;
|
|
74
|
+
const target = session.editing;
|
|
75
|
+
if (!target || target.kind !== "page" || target.v !== PRESENCE_VERSION) continue;
|
|
76
|
+
if (target.epoch !== epoch || target.generation !== generation) continue;
|
|
77
|
+
const spot = decodeSelection(context, target.selection);
|
|
78
|
+
if (!spot) continue;
|
|
79
|
+
if (spot.kind === "text") {
|
|
80
|
+
if (spot.to > spot.from) decorations.push(Decoration.inline(spot.from, spot.to, { class: "page-remote-selection", style: `--page-remote-color:${color}` }));
|
|
81
|
+
decorations.push(Decoration.widget(spot.to, (view) => caretElement(view.dom.ownerDocument, name, color), { side: 1, key: `caret:${person.id}:${session.id}` }));
|
|
82
|
+
} else if (spot.kind === "cell") {
|
|
83
|
+
decorations.push(Decoration.node(spot.from, spot.to, { class: "page-remote-cell", style: `--page-remote-color:${color}` }));
|
|
84
|
+
decorations.push(Decoration.widget(spot.from + 1, (view) => caretElement(view.dom.ownerDocument, name, color), { side: -1, key: `cell:${person.id}:${session.id}` }));
|
|
85
|
+
} else if (spot.kind === "blocks") {
|
|
86
|
+
const [first] = spot.ranges;
|
|
87
|
+
for (const range of spot.ranges) decorations.push(Decoration.node(range.from, range.to, { class: "page-remote-block", style: `--page-remote-color:${color}` }));
|
|
88
|
+
decorations.push(Decoration.widget(first.from + 1, (view) => caretElement(view.dom.ownerDocument, name, color), { side: -1, key: `blocks:${person.id}:${session.id}` }));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return decorations.length ? DecorationSet.create(doc, decorations) : DecorationSet.empty;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Watch a mounted editor: publish this tab's selection while it is focused and
|
|
97
|
+
* draw every permitted peer's, one animation frame after anything changed.
|
|
98
|
+
*
|
|
99
|
+
* Returns the handle the page's teardown releases. In a reader, or on a host
|
|
100
|
+
* without page awareness, nothing is installed and nothing is sent.
|
|
101
|
+
*/
|
|
102
|
+
export function createPageAwareness({ binding, view, store = presenceStore, epoch, composing = null, frame = (fn) => requestAnimationFrame(fn), cancel = (handle) => cancelAnimationFrame(handle) }) {
|
|
103
|
+
if (!binding?.awareness || !view || typeof epoch !== "string" || !epoch) return null;
|
|
104
|
+
styleWithLegacyId(STYLE_ID, STYLE_CSS, STYLE_ID, ["editor-ui"]);
|
|
105
|
+
const doc = view.dom.ownerDocument;
|
|
106
|
+
let disposed = false;
|
|
107
|
+
let focused = true;
|
|
108
|
+
let pending = null;
|
|
109
|
+
const isComposing = () => (composing ? composing() === true : view.composing === true);
|
|
110
|
+
|
|
111
|
+
const supported = () => store.getSnapshot().pageAwareness !== false;
|
|
112
|
+
|
|
113
|
+
const publish = (state = view.state) => {
|
|
114
|
+
if (disposed) return;
|
|
115
|
+
if (!supported() || !focused || isComposing()) {
|
|
116
|
+
store.setTarget(null);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const selection = encodeSelection({ ...binding.awareness.map(), doc: state.doc }, state.selection);
|
|
120
|
+
if (!selection) {
|
|
121
|
+
store.setTarget(null);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
store.setTarget({ kind: "page", v: PRESENCE_VERSION, epoch, generation: binding.awareness.generation(), selection });
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const rebuild = (next) => {
|
|
128
|
+
// During composition the peer's decorations stay where they were: replacing
|
|
129
|
+
// them mid-word would draw over text the person is still forming.
|
|
130
|
+
if (isComposing()) return awarenessKey.getState(view.state);
|
|
131
|
+
return decorationsFor({ binding, doc: next.doc, snapshot: store.getSnapshot(), epoch });
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const plugin = new Plugin({
|
|
135
|
+
key: awarenessKey,
|
|
136
|
+
state: {
|
|
137
|
+
init: (_config, st) => decorationsFor({ binding, doc: st.doc, snapshot: store.getSnapshot(), epoch }),
|
|
138
|
+
apply: (tr, value, _old, next) => {
|
|
139
|
+
// Every transaction a person makes publishes, including one that only moved
|
|
140
|
+
// the caret: the page's dispatch hands the view a state, not a prop update.
|
|
141
|
+
if (tr.docChanged || tr.selectionSet) publish(next);
|
|
142
|
+
return tr.docChanged || tr.getMeta(awarenessKey) ? rebuild(next) : value;
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
props: {
|
|
146
|
+
decorations(st) {
|
|
147
|
+
return awarenessKey.getState(st) ?? DecorationSet.empty;
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const refresh = () => {
|
|
153
|
+
if (disposed || view.isDestroyed) return;
|
|
154
|
+
view.dispatch(view.state.tr.setMeta(awarenessKey, true));
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const schedule = () => {
|
|
158
|
+
if (disposed || pending !== null || view.isDestroyed) return;
|
|
159
|
+
pending = frame(() => {
|
|
160
|
+
pending = null;
|
|
161
|
+
refresh();
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const unsubscribe = store.subscribe((snapshot) => {
|
|
166
|
+
if (snapshot.pageAwareness === false) store.setTarget(null);
|
|
167
|
+
schedule();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const onFocus = () => { focused = true; publish(); schedule(); };
|
|
171
|
+
const onBlur = () => { focused = false; store.setTarget(null); };
|
|
172
|
+
const onHide = () => { store.setTarget(null); };
|
|
173
|
+
const onCompositionEnd = () => { publish(); schedule(); };
|
|
174
|
+
|
|
175
|
+
view.dom.addEventListener("focus", onFocus);
|
|
176
|
+
view.dom.addEventListener("blur", onBlur);
|
|
177
|
+
view.dom.addEventListener("compositionend", onCompositionEnd);
|
|
178
|
+
doc.addEventListener("pagehide", onHide);
|
|
179
|
+
view.updateState(view.state.reconfigure({ plugins: [...view.state.plugins, plugin] }));
|
|
180
|
+
publish();
|
|
181
|
+
schedule();
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
epoch,
|
|
185
|
+
refresh,
|
|
186
|
+
plugin,
|
|
187
|
+
get decorations() {
|
|
188
|
+
return awarenessKey.getState(view.state) ?? DecorationSet.empty;
|
|
189
|
+
},
|
|
190
|
+
get target() {
|
|
191
|
+
return store.target ?? null;
|
|
192
|
+
},
|
|
193
|
+
dispose() {
|
|
194
|
+
if (disposed) return;
|
|
195
|
+
disposed = true;
|
|
196
|
+
if (pending !== null) { cancel(pending); pending = null; }
|
|
197
|
+
unsubscribe();
|
|
198
|
+
view.dom.removeEventListener("focus", onFocus);
|
|
199
|
+
view.dom.removeEventListener("blur", onBlur);
|
|
200
|
+
view.dom.removeEventListener("compositionend", onCompositionEnd);
|
|
201
|
+
doc.removeEventListener("pagehide", onHide);
|
|
202
|
+
store.setTarget(null);
|
|
203
|
+
store.releaseTarget();
|
|
204
|
+
if (!view.isDestroyed) {
|
|
205
|
+
const plugins = view.state.plugins.filter((candidate) => candidate !== plugin);
|
|
206
|
+
view.updateState(view.state.reconfigure({ plugins }));
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Install page awareness on a mounted editor, if this host offers it: the
|
|
214
|
+
* document scope the host announced is what a target's epoch names, so peers on
|
|
215
|
+
* the same document agree without the tab guessing its identity.
|
|
216
|
+
*/
|
|
217
|
+
export async function installPageAwareness({ binding, view, store = presenceStore, meta = null } = {}) {
|
|
218
|
+
if (!binding?.awareness || !view) return null;
|
|
219
|
+
const answer = meta ?? (await hostMeta());
|
|
220
|
+
const extensions = Array.isArray(answer?.extensions) ? answer.extensions : [];
|
|
221
|
+
store.setCapabilities({
|
|
222
|
+
supported: extensions.includes("presence"),
|
|
223
|
+
recordAwareness: extensions.includes("record-awareness-v1"),
|
|
224
|
+
pageAwareness: extensions.includes("page-awareness-v1"),
|
|
225
|
+
});
|
|
226
|
+
if (!extensions.includes("page-awareness-v1")) return null;
|
|
227
|
+
const epoch = answer.document?.awareness?.scope;
|
|
228
|
+
if (typeof epoch !== "string" || !epoch) return null;
|
|
229
|
+
return createPageAwareness({ binding, view, store, epoch });
|
|
230
|
+
}
|
package/src/page/blocks.js
CHANGED
|
@@ -6,6 +6,10 @@ import { model, state, view as pmView, commands, history } from "../vendor/prose
|
|
|
6
6
|
import { schema } from "./schema.js";
|
|
7
7
|
import { REMOTE, newId } from "./model.js";
|
|
8
8
|
import { safeHref, safeSrc } from "./link.js";
|
|
9
|
+
import { flatten, runsOf, unflatten, unitsOf } from "./inline.js";
|
|
10
|
+
import { OPAQUE_MARK, opaqueMark } from "./format-codec.js";
|
|
11
|
+
import { NATIVE_NAMES, nativeAttrs } from "./format-native.js";
|
|
12
|
+
import { DEFAULTS, MARKS, OPAQUE, envelopeAttrs, envelopeRecord, marksAttrs, marksEntries } from "./projection.js";
|
|
9
13
|
|
|
10
14
|
const alignOf = (a) => (a === "left" || a === "right" ? a : null);
|
|
11
15
|
|
|
@@ -340,9 +344,12 @@ export function indentBlocks(st, dispatch) {
|
|
|
340
344
|
const tr = st.tr;
|
|
341
345
|
let end = b;
|
|
342
346
|
for (let k = a; k <= end; k++) end = Math.max(end, ents[k].subEnd);
|
|
347
|
+
let deep = a - 1;
|
|
343
348
|
for (let k = a; k <= end && sameRun(ents[k], first); k++) {
|
|
344
349
|
tr.setNodeMarkup(ents[k].at, null, { ...ents[k].node.attrs, indent: ents[k].node.attrs.indent + 1 });
|
|
350
|
+
deep = k;
|
|
345
351
|
}
|
|
352
|
+
tr.setMeta(MOVE, moveIntent(ents, a, deep, ents[a].at, (e) => (e.i >= a && e.i <= deep ? { indent: e.node.attrs.indent + 1 } : null)));
|
|
346
353
|
dispatch(tr);
|
|
347
354
|
}
|
|
348
355
|
return true;
|
|
@@ -352,10 +359,15 @@ export function indentBlocks(st, dispatch) {
|
|
|
352
359
|
if (dispatch) {
|
|
353
360
|
const { hi } = siblingSpan(ents, a, b);
|
|
354
361
|
const tr = st.tr, from = parentOf(first.node), to = ents[s].node.attrs.id;
|
|
362
|
+
const adopted = [];
|
|
355
363
|
for (let k = a; k <= hi; k++) {
|
|
356
|
-
if (parentOf(ents[k].node)
|
|
364
|
+
if (parentOf(ents[k].node) !== from) continue;
|
|
365
|
+
tr.setNodeMarkup(ents[k].at, null, { ...ents[k].node.attrs, parent: to });
|
|
366
|
+
adopted.push(ents[k].node.attrs.id);
|
|
357
367
|
}
|
|
358
368
|
if (!ents[s].node.attrs.open) tr.setNodeMarkup(ents[s].at, null, { ...ents[s].node.attrs, open: true });
|
|
369
|
+
const intent = moveIntent(ents, a, hi, ents[a].at, (e) => (e.i >= a && e.i <= hi && parentOf(e.node) === from ? { parent: to } : null));
|
|
370
|
+
tr.setMeta(MOVE, { ...intent, ids: adopted, attrs: ents[s].node.attrs.open ? intent.attrs : { ...intent.attrs, [to]: { open: true } } });
|
|
359
371
|
dispatch(tr);
|
|
360
372
|
}
|
|
361
373
|
return true;
|
|
@@ -369,16 +381,22 @@ export function outdentBlocks(st, dispatch) {
|
|
|
369
381
|
const first = ents[a];
|
|
370
382
|
if (isList(first.node) && first.node.attrs.indent > 0) {
|
|
371
383
|
if (dispatch) {
|
|
384
|
+
const { lo, hi } = siblingSpan(ents, a, b);
|
|
372
385
|
const tr = st.tr;
|
|
386
|
+
const lowered = new Set();
|
|
373
387
|
for (let k = a; k <= b;) {
|
|
374
388
|
const e = ents[k];
|
|
375
389
|
if (isList(e.node) && e.node.attrs.indent > 0) {
|
|
376
390
|
for (let m = k; m <= e.subEnd; m++) {
|
|
377
|
-
if (isList(ents[m].node))
|
|
391
|
+
if (isList(ents[m].node)) {
|
|
392
|
+
tr.setNodeMarkup(ents[m].at, null, { ...ents[m].node.attrs, indent: ents[m].node.attrs.indent - 1 });
|
|
393
|
+
lowered.add(m);
|
|
394
|
+
}
|
|
378
395
|
}
|
|
379
396
|
}
|
|
380
397
|
k = e.subEnd + 1;
|
|
381
398
|
}
|
|
399
|
+
tr.setMeta(MOVE, moveIntent(ents, lo, hi, ents[lo].at, (e) => (lowered.has(e.i) ? { indent: e.node.attrs.indent - 1 } : null)));
|
|
382
400
|
dispatch(tr);
|
|
383
401
|
}
|
|
384
402
|
return true;
|
|
@@ -395,6 +413,7 @@ export function outdentBlocks(st, dispatch) {
|
|
|
395
413
|
if (hi === end) {
|
|
396
414
|
const tr = st.tr;
|
|
397
415
|
for (let k = lo; k <= hi; k++) { const c = attrs(ents[k]); if (c) tr.setNodeMarkup(ents[k].at, null, { ...ents[k].node.attrs, ...c }); }
|
|
416
|
+
tr.setMeta(MOVE, moveIntent(ents, lo, hi, ents[lo].at, attrs));
|
|
398
417
|
dispatch(tr);
|
|
399
418
|
} else dispatch(moveSpanTr(st, ents, lo, hi, ents[end].end, attrs).scrollIntoView());
|
|
400
419
|
}
|
|
@@ -481,7 +500,13 @@ export function exitToggleOnEmpty(st, dispatch) {
|
|
|
481
500
|
const i = $from.index(0);
|
|
482
501
|
const ti = ents.findIndex((x) => x.node.attrs.id === t);
|
|
483
502
|
if (ti < 0 || ents[ti].subEnd !== i) return false;
|
|
484
|
-
if (dispatch)
|
|
503
|
+
if (dispatch) {
|
|
504
|
+
const id = ents[i].node.attrs.id, to = parentOf(ents[ti].node);
|
|
505
|
+
const prev = i > 0 ? ents[i - 1].node : null;
|
|
506
|
+
const tr = st.tr.setNodeMarkup(ents[i].at, null, { ...$from.parent.attrs, parent: to });
|
|
507
|
+
tr.setMeta(MOVE, { ids: [id], afterId: prev ? prev.attrs.id : null, parent: parentOf(prev ?? ents[i].node), attrs: { [id]: { parent: to } } });
|
|
508
|
+
dispatch(tr);
|
|
509
|
+
}
|
|
485
510
|
return true;
|
|
486
511
|
}
|
|
487
512
|
|
|
@@ -1024,8 +1049,41 @@ export function flattenPastedHTML(html, doc = globalThis.document) {
|
|
|
1024
1049
|
const numberAttr = (value, min = 0) => value !== null && value !== "" && Number.isInteger(+value) && +value >= min ? +value : null;
|
|
1025
1050
|
const parentAttr = (dom) => dom.getAttribute("data-page-parent") || null;
|
|
1026
1051
|
|
|
1052
|
+
// A copied opaque mark is not one of the schema's marks, so the rules of the
|
|
1053
|
+
// clipboard parser resolve mark names through a view of the schema that holds
|
|
1054
|
+
// the internal one; every node a parse builds is the schema's own.
|
|
1055
|
+
const opaqueType = opaqueMark([]).type;
|
|
1056
|
+
const parseSchema = Object.create(schema, { marks: { value: { ...schema.marks, [OPAQUE_MARK]: opaqueType } } });
|
|
1057
|
+
|
|
1058
|
+
// The record of a pasted block envelope, or null when it is not one this
|
|
1059
|
+
// runtime may keep: a malformed, oversized or hostile envelope, one naming a
|
|
1060
|
+
// known block type, or one with no type of its own. A pasted element with no
|
|
1061
|
+
// usable record is plain text, never a record this runtime made up.
|
|
1062
|
+
const pastedRecord = (dom) => {
|
|
1063
|
+
let record;
|
|
1064
|
+
try { record = envelopeRecord(dom); } catch { return null; }
|
|
1065
|
+
if (!record || typeof record.t !== "string" || record.t === "opaque_block" || Object.hasOwn(DEFAULTS, record.t)) return null;
|
|
1066
|
+
return record;
|
|
1067
|
+
};
|
|
1068
|
+
const pastedOpaque = (dom) => {
|
|
1069
|
+
const record = pastedRecord(dom);
|
|
1070
|
+
if (!record) return false;
|
|
1071
|
+
return { raw: record, parent: parentAttr(dom) };
|
|
1072
|
+
};
|
|
1073
|
+
const pastedOpaqueContent = (dom) => {
|
|
1074
|
+
const record = pastedRecord(dom);
|
|
1075
|
+
if (!record || typeof record.c !== "string" || !record.c) return model.Fragment.empty;
|
|
1076
|
+
const units = unitsOf(record.m, record.c.length);
|
|
1077
|
+
return model.Fragment.from(unflatten(schema, record.c, units, schema.nodes.opaque_block));
|
|
1078
|
+
};
|
|
1079
|
+
const pastedOpaqueMarks = (dom) => {
|
|
1080
|
+
try { return { entries: marksEntries(dom.getAttribute(MARKS)) }; } catch { return false; }
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1027
1083
|
// The clipboard parser: the schema has toDOM only, so paste gets its own rules.
|
|
1028
|
-
export const clipboardParser = new model.DOMParser(
|
|
1084
|
+
export const clipboardParser = new model.DOMParser(parseSchema, [
|
|
1085
|
+
{ tag: `div[${OPAQUE}]`, node: "opaque_block", getAttrs: pastedOpaque, getContent: pastedOpaqueContent },
|
|
1086
|
+
{ tag: `span[${MARKS}]`, mark: OPAQUE_MARK, getAttrs: pastedOpaqueMarks },
|
|
1029
1087
|
{ tag: "div[data-page-li]", node: "list_item", getAttrs: (d) => ({ kind: d.getAttribute("data-kind") || "bullet", indent: +d.getAttribute("data-indent") || 0, checked: d.getAttribute("data-checked") === "true", parent: parentAttr(d) }) },
|
|
1030
1088
|
{ tag: "div.page-li", node: "list_item", getAttrs: (d) => ({ kind: d.getAttribute("data-kind") || "bullet", indent: +d.getAttribute("data-indent") || 0, checked: d.getAttribute("data-checked") === "true", parent: parentAttr(d) }) },
|
|
1031
1089
|
{ tag: "div[data-page-toggle]", node: "toggle", getAttrs: (d) => ({ id: d.getAttribute("data-page-toggle"), open: d.getAttribute("data-open") === "true", parent: parentAttr(d) }) },
|
|
@@ -1061,6 +1119,10 @@ export const clipboardParser = new model.DOMParser(schema, [
|
|
|
1061
1119
|
{ tag: "del", mark: "strike" },
|
|
1062
1120
|
{ tag: "code", mark: "code" },
|
|
1063
1121
|
{ tag: "a[href]", mark: "link", getAttrs: (d) => { const href = safeHref(d.getAttribute("href")); return href ? { href } : false; } },
|
|
1122
|
+
{ tag: "blockquote", node: "quote", getAttrs: (d) => ({ parent: parentAttr(d) }) },
|
|
1123
|
+
{ tag: "u", mark: "underline" },
|
|
1124
|
+
{ tag: "mark", mark: "highlight", getAttrs: (d) => nativeAttrs("highlight", { color: d.getAttribute("data-color") }) ?? false },
|
|
1125
|
+
{ tag: "span[data-color]", mark: "text_color", getAttrs: (d) => nativeAttrs("text_color", { color: d.getAttribute("data-color") }) ?? false },
|
|
1064
1126
|
]);
|
|
1065
1127
|
|
|
1066
1128
|
// Every pasted block arrives with no id and no pos (identityPlugin mints and
|
|
@@ -1115,7 +1177,24 @@ class ClipboardSerializer extends model.DOMSerializer {
|
|
|
1115
1177
|
}
|
|
1116
1178
|
}
|
|
1117
1179
|
const base = model.DOMSerializer.fromSchema(schema);
|
|
1118
|
-
|
|
1180
|
+
|
|
1181
|
+
// A copied block this runtime has no schema for keeps its record in the
|
|
1182
|
+
// projection's block envelope, with its own text as the readable fallback, and
|
|
1183
|
+
// a copied opaque mark keeps its descriptors in the marks envelope. The record
|
|
1184
|
+
// is the one the saved article keeps: its own type, every field it carried, and
|
|
1185
|
+
// its text with its marks.
|
|
1186
|
+
const opaqueRecord = (node) => {
|
|
1187
|
+
const { t, ...rest } = node.attrs.raw ?? {};
|
|
1188
|
+
const { c, m } = flatten(node.content);
|
|
1189
|
+
if (!c) return { t, ...rest };
|
|
1190
|
+
return { t, ...rest, c, ...(m.some(Boolean) ? { m: runsOf(m) } : {}) };
|
|
1191
|
+
};
|
|
1192
|
+
const opaqueNode = (node) => ["div", { "data-type": "newer-format", ...envelopeAttrs(opaqueRecord(node)) }, 0];
|
|
1193
|
+
const opaqueMarks = (mark) => ["span", marksAttrs(mark.attrs.entries), 0];
|
|
1194
|
+
const clipboardNodes = { ...base.nodes, opaque_block: opaqueNode };
|
|
1195
|
+
const clipboardMarks = { ...base.marks, [OPAQUE_MARK]: opaqueMarks };
|
|
1196
|
+
|
|
1197
|
+
export const clipboardSerializer = new ClipboardSerializer(clipboardNodes, clipboardMarks);
|
|
1119
1198
|
|
|
1120
1199
|
// Several blocks pasted into a closed toggle's summary join its body, so the
|
|
1121
1200
|
// toggle opens first instead of hiding what was just pasted.
|
|
@@ -1128,10 +1207,38 @@ function pasteIntoClosedSummary(view, _event, slice) {
|
|
|
1128
1207
|
return true;
|
|
1129
1208
|
}
|
|
1130
1209
|
|
|
1210
|
+
// The four formats pasted in from elsewhere are floor-1 content too: a slice
|
|
1211
|
+
// that holds one asks the gate before it is inserted, and text waits in the
|
|
1212
|
+
// clipboard rather than being dropped when the answer is a refusal.
|
|
1213
|
+
function writesFormat(slice) {
|
|
1214
|
+
let found = false;
|
|
1215
|
+
slice?.content?.descendants((node) => {
|
|
1216
|
+
if (found) return false;
|
|
1217
|
+
if (node.type === N.quote) found = true;
|
|
1218
|
+
else if (node.isText && node.marks.some((mark) => NATIVE_NAMES.has(mark.type.name))) found = true;
|
|
1219
|
+
return !found;
|
|
1220
|
+
});
|
|
1221
|
+
return found;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
function pasteFormat(view, event, slice) {
|
|
1225
|
+
const from = view.state.selection.from, to = view.state.selection.to;
|
|
1226
|
+
const plain = () => {
|
|
1227
|
+
const text = slice.content.textBetween(0, slice.content.size, "\n", " ");
|
|
1228
|
+
if (!view.isDestroyed && text) view.dispatch(view.state.tr.insertText(text, from, to));
|
|
1229
|
+
return false;
|
|
1230
|
+
};
|
|
1231
|
+
import("./format-native-commands.js").then(({ pasteFormats }) => pasteFormats(view, slice, from, to), plain);
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1131
1235
|
export function pastePlugin() {
|
|
1132
1236
|
return new Plugin({
|
|
1133
1237
|
props: {
|
|
1134
|
-
handlePaste
|
|
1238
|
+
handlePaste(view, event, slice) {
|
|
1239
|
+
if (writesFormat(slice)) return pasteFormat(view, event, slice);
|
|
1240
|
+
return pasteIntoClosedSummary(view, event, slice);
|
|
1241
|
+
},
|
|
1135
1242
|
clipboardSerializer,
|
|
1136
1243
|
transformPastedHTML: (html) => flattenPastedHTML(html),
|
|
1137
1244
|
clipboardParser,
|
package/src/page/blocksel.js
CHANGED
|
@@ -8,6 +8,7 @@ import { schema } from "./schema.js";
|
|
|
8
8
|
import { outline, siblingSpan, touchedBlocks, BlockSelection, STEP } from "./blocks.js";
|
|
9
9
|
import { keysBetween, newId, orderedIds, REMOTE } from "./model.js";
|
|
10
10
|
import { announce } from "./announce.js";
|
|
11
|
+
import { rectKey } from "./table-rect-key.js";
|
|
11
12
|
|
|
12
13
|
const { Selection, TextSelection, NodeSelection, Plugin, PluginKey } = state;
|
|
13
14
|
const N = schema.nodes;
|
|
@@ -145,14 +146,29 @@ export function exitToText(st, dispatch) {
|
|
|
145
146
|
return true;
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
// The top-level table holding both ends of the selection, as block boundaries.
|
|
150
|
+
const tableAround = ({ $from, $to }) => {
|
|
151
|
+
if ($from.depth < 2 || $to.depth < 2) return null;
|
|
152
|
+
const node = $from.node(1);
|
|
153
|
+
return node.type.name === "table" && $to.node(1) === node ? { from: $from.before(1), to: $from.after(1) } : null;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Mod-a widens one step at a time: the text of the block or table cell, then the table
|
|
157
|
+
// around a cell, then every block.
|
|
148
158
|
export function selectAllBlocks(st, dispatch) {
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
159
|
+
const sel = st.selection;
|
|
160
|
+
const { $from, $to } = sel;
|
|
161
|
+
const inText = $from.parent.isTextblock && $from.sameParent($to) && !rectKey.getState(st);
|
|
162
|
+
const whole = inText && $from.parentOffset === 0 && $to.parentOffset === $to.parent.content.size;
|
|
163
|
+
if (inText && !whole && $from.parent.content.size) {
|
|
153
164
|
if (dispatch) dispatch(st.tr.setSelection(TextSelection.create(st.doc, $from.start(), $from.end())));
|
|
154
165
|
return true;
|
|
155
166
|
}
|
|
167
|
+
const table = tableAround(sel);
|
|
168
|
+
if (table && !(sel instanceof BlockSelection && sel.from === table.from && sel.to === table.to)) {
|
|
169
|
+
if (dispatch) dispatch(st.tr.setSelection(new BlockSelection(st.doc.resolve(table.from), st.doc.resolve(table.to))));
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
156
172
|
if (dispatch) dispatch(st.tr.setSelection(new BlockSelection(st.doc.resolve(0), st.doc.resolve(st.doc.content.size))));
|
|
157
173
|
return true;
|
|
158
174
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The small board pieces the checklist and the board view and panel share, so
|
|
2
|
+
// the board's own view part can load without them.
|
|
3
|
+
export function el(doc, tag, cls, attrs = {}, text = null) {
|
|
4
|
+
const node = doc.createElement(tag);
|
|
5
|
+
if (cls) node.className = cls;
|
|
6
|
+
for (const [key, value] of Object.entries(attrs)) if (value !== null && value !== undefined && value !== false) node.setAttribute(key, value === true ? "" : String(value));
|
|
7
|
+
if (text !== null) node.textContent = text;
|
|
8
|
+
return node;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const initialOf = (name) => ([...String(name).trim()][0] ?? "?").toUpperCase();
|
|
12
|
+
|
|
13
|
+
export const cardFace = (doc, name) => el(doc, "span", "page-board-face", { "aria-hidden": "true" }, initialOf(name));
|
package/src/page/board-panel.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { state } from "../vendor/prosemirror.vendor.js";
|
|
2
1
|
import { schema } from "./schema.js";
|
|
3
2
|
import { announce } from "./announce.js";
|
|
4
|
-
import { BOARD_ICONS,
|
|
3
|
+
import { BOARD_ICONS, iconButton, boardOf, dueLabel, dueState, todayKey, placeAfter } from "./board-view.js";
|
|
4
|
+
import { el, cardFace } from "./board-face.js";
|
|
5
|
+
import { cardPanels } from "./board-views.js";
|
|
5
6
|
|
|
6
7
|
const NARROW = "(max-width: 1039.98px)";
|
|
7
8
|
const isTyping = (target) => /^(INPUT|TEXTAREA|SELECT)$/.test(target?.tagName ?? "");
|
|
8
|
-
const panels = new WeakMap();
|
|
9
9
|
let count = 0;
|
|
10
10
|
|
|
11
11
|
export function cardPanel(view) {
|
|
12
|
-
if (!
|
|
13
|
-
return
|
|
12
|
+
if (!cardPanels.has(view)) cardPanels.set(view, new CardPanel(view));
|
|
13
|
+
return cardPanels.get(view);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export class CardPanel {
|
|
@@ -339,15 +339,3 @@ export class CardPanel {
|
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
|
-
|
|
343
|
-
export function boardPlugin() {
|
|
344
|
-
return new state.Plugin({
|
|
345
|
-
view: (view) => ({
|
|
346
|
-
update: () => panels.get(view)?.sync(),
|
|
347
|
-
destroy: () => {
|
|
348
|
-
panels.get(view)?.hide(false, false);
|
|
349
|
-
panels.delete(view);
|
|
350
|
-
},
|
|
351
|
-
}),
|
|
352
|
-
});
|
|
353
|
-
}
|
package/src/page/board-style.js
CHANGED
|
@@ -21,7 +21,6 @@ export const BOARD_CSS = `
|
|
|
21
21
|
.page-board-meta { display: flex; align-items: center; gap: 8px; margin-top: 8px; color: var(--page-ink-soft); font-size: 13px; line-height: 20px; }
|
|
22
22
|
.page-board-owner { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
|
|
23
23
|
.page-board-owner-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
24
|
-
.page-board-face { flex: none; display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; background: var(--page-code-bg); box-shadow: inset 0 0 0 1px var(--page-rule); color: var(--page-ink-soft); font-size: 10.5px; font-weight: 600; line-height: 1; }
|
|
25
24
|
.page-board-due { flex: none; display: inline-flex; align-items: center; gap: 6px; margin-inline-start: auto; white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
26
25
|
.page-board-due[data-due="today"] { color: var(--page-ink); font-weight: 500; }
|
|
27
26
|
.page-board-due[data-due="overdue"] { color: var(--page-ink); }
|
|
@@ -95,14 +94,6 @@ export const BOARD_CSS = `
|
|
|
95
94
|
.page-board-control .page-board-due + .page-board-due-note { margin-inline-start: auto; }
|
|
96
95
|
.page-board-control .page-board-due-note + .i { margin-inline-start: 0; }
|
|
97
96
|
.page-board-date { border: 1px solid var(--page-pop-line); cursor: text; }
|
|
98
|
-
.page-board-owners { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-inline: 8px; }
|
|
99
|
-
.page-board-people { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
100
|
-
.page-board-person { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px 0 4px; border: 1px solid var(--page-rule); border-radius: 14px; background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 13px; cursor: pointer; transition: background-color var(--page-fade), border-color var(--page-fade); }
|
|
101
|
-
.page-board-person.is-none { padding-inline-start: 10px; color: var(--page-pop-muted); }
|
|
102
|
-
.page-board-person:hover { background: var(--page-pop-hover); }
|
|
103
|
-
.page-board-person[aria-pressed="true"] { border-color: var(--page-accent); background: var(--page-selection); color: var(--page-pop-ink); }
|
|
104
|
-
.page-board-owner-input { width: 100%; height: 32px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--page-pop-line); border-radius: 7px; background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 14px; }
|
|
105
|
-
.page-board-owner-input::placeholder { color: var(--page-ink-faint); }
|
|
106
97
|
.page-board-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 20px; }
|
|
107
98
|
.page-board-delete { margin-inline-start: auto; border-color: transparent; color: var(--page-pop-muted); }
|
|
108
99
|
.page-board-delete:hover { background: var(--page-pop-hover); color: var(--page-danger); }
|
|
@@ -135,12 +126,10 @@ export const BOARD_CSS = `
|
|
|
135
126
|
.page-board-rename { height: 44px; margin-block: -12px; font-size: 16px; }
|
|
136
127
|
.page-board-panel { border-width: 1px 0 0; }
|
|
137
128
|
.page-board-panel-title { margin-top: -8px; padding-block: 8px; }
|
|
138
|
-
.page-board-control
|
|
129
|
+
.page-board-control { font-size: 16px; }
|
|
139
130
|
.page-board-label { font-size: 15px; }
|
|
140
|
-
.page-board-person { height: 44px; border-radius: 22px; }
|
|
141
|
-
.page-board-owner-input { height: 44px; }
|
|
142
131
|
}
|
|
143
132
|
@media (prefers-reduced-motion: reduce) {
|
|
144
|
-
.page-board-icon, .page-board-card, .page-board-add, .page-board-addcol, .page-board-btn, .page-board-control
|
|
133
|
+
.page-board-icon, .page-board-card, .page-board-add, .page-board-addcol, .page-board-btn, .page-board-control { transition: none; }
|
|
145
134
|
}
|
|
146
135
|
`;
|
package/src/page/board-view.js
CHANGED
|
@@ -2,6 +2,7 @@ import { schema } from "./schema.js";
|
|
|
2
2
|
import { orderedIds } from "./model.js";
|
|
3
3
|
import { announce } from "./announce.js";
|
|
4
4
|
import { BoardDrag } from "./board-drag.js";
|
|
5
|
+
import { el, cardFace } from "./board-face.js";
|
|
5
6
|
|
|
6
7
|
const N = schema.nodes;
|
|
7
8
|
const boardViews = new WeakMap();
|
|
@@ -24,14 +25,6 @@ export const BOARD_ICONS = {
|
|
|
24
25
|
restore: svg('<path d="M3.5 8a4.5 4.5 0 1 0 1.32-3.18M3.5 2.75v2.5H6"/>'),
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
export function el(doc, tag, cls, attrs = {}, text = null) {
|
|
28
|
-
const node = doc.createElement(tag);
|
|
29
|
-
if (cls) node.className = cls;
|
|
30
|
-
for (const [key, value] of Object.entries(attrs)) if (value !== null && value !== undefined && value !== false) node.setAttribute(key, value === true ? "" : String(value));
|
|
31
|
-
if (text !== null) node.textContent = text;
|
|
32
|
-
return node;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
28
|
export function iconButton(doc, cls, icon, label, attrs = {}) {
|
|
36
29
|
const button = el(doc, "button", cls, { type: "button", ...attrs });
|
|
37
30
|
button.innerHTML = BOARD_ICONS[icon];
|
|
@@ -61,7 +54,6 @@ export function dueLabel(due, today = todayKey(), long = false) {
|
|
|
61
54
|
const sameYear = due.slice(0, 4) === today.slice(0, 4);
|
|
62
55
|
return new Date(`${due}T00:00:00Z`).toLocaleDateString("en-US", { ...(long ? { weekday: "short" } : {}), month: "short", day: "numeric", ...(sameYear ? {} : { year: "numeric" }), timeZone: "UTC" });
|
|
63
56
|
}
|
|
64
|
-
export const initialOf = (name) => ([...String(name).trim()][0] ?? "?").toUpperCase();
|
|
65
57
|
export const placeAfter = (ids, index) => (index >= 2 ? ids[index - 2] : null);
|
|
66
58
|
const countOf = (doc, n) => el(doc, "span", "page-board-count", { "aria-label": `${n} ${n === 1 ? "card" : "cards"}` }, String(n));
|
|
67
59
|
const isTyping = (target) => /^(INPUT|TEXTAREA|SELECT)$/.test(target?.tagName ?? "");
|
|
@@ -75,8 +67,6 @@ function dueBadge(doc, due) {
|
|
|
75
67
|
return badge;
|
|
76
68
|
}
|
|
77
69
|
|
|
78
|
-
export const cardFace = (doc, name) => el(doc, "span", "page-board-face", { "aria-hidden": "true" }, initialOf(name));
|
|
79
|
-
|
|
80
70
|
export class BoardView {
|
|
81
71
|
constructor(node, view, getPos, { commands = null, undo = null, redo = null, panel = null } = {}) {
|
|
82
72
|
this.node = node;
|
package/src/page/board.js
CHANGED
|
@@ -20,10 +20,10 @@ export function makeBoard(names = BOARD_COLUMNS) {
|
|
|
20
20
|
// The board takes the place of an empty line, otherwise it goes after the
|
|
21
21
|
// line's whole subtree at the line's own level. An empty paragraph follows it
|
|
22
22
|
// and holds the caret, so typing goes on below the board.
|
|
23
|
-
export function insertBoardNode(st, dispatch, board = makeBoard(), remove = null) {
|
|
23
|
+
export function insertBoardNode(st, dispatch, board = makeBoard(), remove = null, index = null) {
|
|
24
24
|
const tr = st.tr;
|
|
25
25
|
if (remove) tr.delete(remove.from, remove.to);
|
|
26
|
-
|
|
26
|
+
index ??= tr.doc.resolve(tr.mapping.map(st.selection.head)).index(0);
|
|
27
27
|
const ents = outline(tr.doc);
|
|
28
28
|
const line = tr.doc.child(index);
|
|
29
29
|
const empty = line.isTextblock && line.content.size === 0;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// The page's boot code in one module: the page code and the chrome, recovery and title
|
|
2
|
+
// code beside it arrive through this file's static graph, and the plugin fetches it with
|
|
3
|
+
// one dynamic import. Loading the parts together is what keeps a module they share from
|
|
4
|
+
// being linked by two dynamic imports at once, and it is one download for a boot that
|
|
5
|
+
// needs them all. The parts a page chooses from its own tag (a table, a board, media, a
|
|
6
|
+
// tool) stay separate dynamic imports, issued once this has resolved.
|
|
7
|
+
//
|
|
8
|
+
// The editor is a download of its own, and it starts here rather than after this file
|
|
9
|
+
// so its fetch runs beside the page code's. It is not awaited: the page code, and the
|
|
10
|
+
// room participant the binding registers, must be ready while the editor is still on
|
|
11
|
+
// the wire. Its static graph is linked by the time this body runs, so the vendor it
|
|
12
|
+
// shares with the page code is already in cache when its own link begins.
|
|
13
|
+
export { installAdoption, ADOPTION_TIMEOUT_MS } from "./adopt-boot.js";
|
|
14
|
+
export { classifyFile, readBack } from "./adopt.js";
|
|
15
|
+
export { mergePageModels } from "./merge.js";
|
|
16
|
+
export { canon } from "./projection.js";
|
|
17
|
+
export { renderFromTags } from "./save-projection.js";
|
|
18
|
+
export { blocksFromYValue } from "./ybind/decode.js";
|
|
19
|
+
export { createYPageBinding } from "./ybind/binding.js";
|
|
20
|
+
export { yValueOf } from "./ybind/provider.js";
|
|
21
|
+
export { seedPageFromArticle } from "./ybind/migrate.js";
|
|
22
|
+
export { holdSlashRange, heldSlashRange } from "./chrome.js";
|
|
23
|
+
export { createRecoverList, focusBlock } from "./recover.js";
|
|
24
|
+
export { observeDocumentTitle } from "./document-title-runtime.js";
|
|
25
|
+
export { createFeatureLoader } from "./feature-loader.js";
|
|
26
|
+
import("./editor.js").catch(() => null);
|