@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,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upload-ui.js — the shared row for one upload task: name, progress, actions.
|
|
3
|
+
*
|
|
4
|
+
* `upload-task.js` owns the upload; this module only draws what that task says and
|
|
5
|
+
* forwards a person's clicks back to it. Journal and Kanban mount the same row, so
|
|
6
|
+
* neither duplicates the controls and neither can drift from the other's behaviour.
|
|
7
|
+
*
|
|
8
|
+
* const unmount = await clay.upload.mount(task, container);
|
|
9
|
+
* unmount();
|
|
10
|
+
*
|
|
11
|
+
* Everything it draws is `clay="editor-ui"`, so none of it reaches a saved file or a
|
|
12
|
+
* sync frame. Nothing here is imported by `upload.js` statically: the row and its
|
|
13
|
+
* stylesheet load the first time somebody mounts one, so a document that never
|
|
14
|
+
* uploads never downloads them.
|
|
15
|
+
*
|
|
16
|
+
* The look is a set of `--clay-upload-*` custom properties, each with a default in
|
|
17
|
+
* its own `var()`. A container that sets one — `.journal-drop { --clay-upload-ink:
|
|
18
|
+
* #333 }` — restyles the row without replacing any of its behaviour.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { canRetry } from "./upload-task.js";
|
|
22
|
+
|
|
23
|
+
const CSS = `.clay-upload {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-wrap: wrap;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: var(--clay-upload-gap, 8px);
|
|
28
|
+
padding: var(--clay-upload-padding, 8px 10px);
|
|
29
|
+
font-family: var(--clay-upload-font, inherit);
|
|
30
|
+
font-size: var(--clay-upload-font-size, 13px);
|
|
31
|
+
line-height: var(--clay-upload-line-height, 1.4);
|
|
32
|
+
color: var(--clay-upload-ink, #171A1F);
|
|
33
|
+
text-align: left;
|
|
34
|
+
background: var(--clay-upload-bg, transparent);
|
|
35
|
+
border: var(--clay-upload-border-width, 0) solid var(--clay-upload-border, #D8D8DE);
|
|
36
|
+
border-radius: var(--clay-upload-radius, 6px);
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
max-width: 100%;
|
|
39
|
+
}
|
|
40
|
+
.clay-upload *, .clay-upload *::before, .clay-upload *::after { box-sizing: border-box; }
|
|
41
|
+
.clay-upload__name {
|
|
42
|
+
flex: 1 1 8em;
|
|
43
|
+
min-width: 0;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
text-overflow: ellipsis;
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
font-weight: var(--clay-upload-name-weight, 500);
|
|
48
|
+
color: var(--clay-upload-ink, #171A1F);
|
|
49
|
+
}
|
|
50
|
+
.clay-upload__progress {
|
|
51
|
+
flex: 2 1 6em;
|
|
52
|
+
width: auto;
|
|
53
|
+
height: var(--clay-upload-progress-height, 6px);
|
|
54
|
+
margin: 0;
|
|
55
|
+
appearance: none;
|
|
56
|
+
-webkit-appearance: none;
|
|
57
|
+
border: 0;
|
|
58
|
+
border-radius: calc(var(--clay-upload-progress-height, 6px) / 2);
|
|
59
|
+
background: var(--clay-upload-track, rgba(23, 26, 31, .14));
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
.clay-upload__progress::-webkit-progress-bar { background: var(--clay-upload-track, rgba(23, 26, 31, .14)); }
|
|
63
|
+
.clay-upload__progress::-webkit-progress-value { background: var(--clay-upload-fill, #2F6FEB); }
|
|
64
|
+
.clay-upload__progress::-moz-progress-bar { background: var(--clay-upload-fill, #2F6FEB); }
|
|
65
|
+
.clay-upload__label {
|
|
66
|
+
flex: 0 1 auto;
|
|
67
|
+
font-variant-numeric: tabular-nums;
|
|
68
|
+
color: var(--clay-upload-muted, #5C6069);
|
|
69
|
+
}
|
|
70
|
+
.clay-upload__status {
|
|
71
|
+
position: absolute;
|
|
72
|
+
width: 1px;
|
|
73
|
+
height: 1px;
|
|
74
|
+
margin: -1px;
|
|
75
|
+
padding: 0;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
clip-path: inset(50%);
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
border: 0;
|
|
80
|
+
}
|
|
81
|
+
.clay-upload__actions { display: flex; flex: none; align-items: center; gap: var(--clay-upload-action-gap, 4px); }
|
|
82
|
+
.clay-upload__button {
|
|
83
|
+
min-height: var(--clay-upload-button-height, 28px);
|
|
84
|
+
padding: var(--clay-upload-button-padding, 4px 10px);
|
|
85
|
+
font: inherit;
|
|
86
|
+
font-weight: var(--clay-upload-button-weight, 500);
|
|
87
|
+
color: var(--clay-upload-button-ink, #2F6FEB);
|
|
88
|
+
background: var(--clay-upload-button-bg, transparent);
|
|
89
|
+
border: 1px solid var(--clay-upload-button-border, transparent);
|
|
90
|
+
border-radius: var(--clay-upload-button-radius, 4px);
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
}
|
|
94
|
+
.clay-upload__button:hover { background: var(--clay-upload-button-bg-hover, rgba(47, 111, 235, .1)); }
|
|
95
|
+
.clay-upload__button:focus-visible { outline: 2px solid var(--clay-upload-fill, #2F6FEB); outline-offset: 2px; }
|
|
96
|
+
.clay-upload__button[hidden] { display: none; }
|
|
97
|
+
.clay-upload[data-clay-upload-state="failed"] .clay-upload__label { color: var(--clay-upload-failure-ink, #B3261E); }
|
|
98
|
+
@media (pointer: coarse) {
|
|
99
|
+
.clay-upload__button { min-height: var(--clay-upload-button-height, 44px); }
|
|
100
|
+
}
|
|
101
|
+
@media (prefers-reduced-motion: reduce) {
|
|
102
|
+
.clay-upload__progress { transition: none; }
|
|
103
|
+
}
|
|
104
|
+
@media (prefers-color-scheme: dark) {
|
|
105
|
+
.clay-upload { color: var(--clay-upload-ink, #E9E9EC); border-color: var(--clay-upload-border, #3A3D45); }
|
|
106
|
+
.clay-upload__name { color: var(--clay-upload-ink, #E9E9EC); }
|
|
107
|
+
.clay-upload__label { color: var(--clay-upload-muted, #A2A6AE); }
|
|
108
|
+
.clay-upload__progress { background: var(--clay-upload-track, rgba(233, 233, 236, .2)); }
|
|
109
|
+
.clay-upload__progress::-webkit-progress-bar { background: var(--clay-upload-track, rgba(233, 233, 236, .2)); }
|
|
110
|
+
.clay-upload__button { color: var(--clay-upload-button-ink, #8AB4FF); }
|
|
111
|
+
.clay-upload[data-clay-upload-state="failed"] .clay-upload__label { color: var(--clay-upload-failure-ink, #FFB4AB); }
|
|
112
|
+
}
|
|
113
|
+
@media print { .clay-upload { display: none; } }`;
|
|
114
|
+
|
|
115
|
+
function stylesOnce() {
|
|
116
|
+
if (document.getElementById("clay-upload-style")) return;
|
|
117
|
+
const style = document.createElement("style");
|
|
118
|
+
style.id = "clay-upload-style";
|
|
119
|
+
style.setAttribute("clay", "editor-ui");
|
|
120
|
+
style.textContent = CSS;
|
|
121
|
+
document.head.append(style);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function percentOf({ loaded, total, percent }) {
|
|
125
|
+
if (typeof percent === "number") return percent;
|
|
126
|
+
if (typeof total === "number" && total > 0) return Math.round((loaded / total) * 100);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Draw one upload task into a container, and stop when told to.
|
|
132
|
+
*
|
|
133
|
+
* @param {Object} task - from `clay.upload.start`
|
|
134
|
+
* @param {Element|string} container - where the row goes
|
|
135
|
+
* @returns {{unmount: function}}
|
|
136
|
+
*/
|
|
137
|
+
export function mount(task, container) {
|
|
138
|
+
if (!task || typeof task.subscribe !== "function") {
|
|
139
|
+
throw new TypeError("clay.upload.mount needs an upload task");
|
|
140
|
+
}
|
|
141
|
+
const host = typeof container === "string" ? document.querySelector(container) : container;
|
|
142
|
+
if (!host || host.nodeType !== 1) {
|
|
143
|
+
throw new TypeError("clay.upload.mount needs a container");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
stylesOnce();
|
|
147
|
+
|
|
148
|
+
const name = task.file?.name || "File";
|
|
149
|
+
|
|
150
|
+
const root = document.createElement("div");
|
|
151
|
+
root.className = "clay-upload";
|
|
152
|
+
root.setAttribute("clay", "editor-ui");
|
|
153
|
+
root.setAttribute("data-clay-upload-state", "uploading");
|
|
154
|
+
root.innerHTML =
|
|
155
|
+
`<span class="clay-upload__name"></span>` +
|
|
156
|
+
`<progress class="clay-upload__progress"></progress>` +
|
|
157
|
+
`<span class="clay-upload__label"></span>` +
|
|
158
|
+
`<span class="clay-upload__status" role="status" aria-live="polite" aria-atomic="true"></span>` +
|
|
159
|
+
`<span class="clay-upload__actions">` +
|
|
160
|
+
`<button type="button" class="clay-upload__button clay-upload__cancel">Cancel</button>` +
|
|
161
|
+
`<button type="button" class="clay-upload__button clay-upload__retry" hidden>Retry</button>` +
|
|
162
|
+
`<button type="button" class="clay-upload__button clay-upload__dismiss">Dismiss</button>` +
|
|
163
|
+
`</span>`;
|
|
164
|
+
|
|
165
|
+
const nameEl = root.querySelector(".clay-upload__name");
|
|
166
|
+
const progressEl = root.querySelector(".clay-upload__progress");
|
|
167
|
+
const labelEl = root.querySelector(".clay-upload__label");
|
|
168
|
+
const statusEl = root.querySelector(".clay-upload__status");
|
|
169
|
+
const cancelEl = root.querySelector(".clay-upload__cancel");
|
|
170
|
+
const retryEl = root.querySelector(".clay-upload__retry");
|
|
171
|
+
const dismissEl = root.querySelector(".clay-upload__dismiss");
|
|
172
|
+
|
|
173
|
+
nameEl.textContent = name;
|
|
174
|
+
progressEl.setAttribute("aria-label", `Upload progress for ${name}`);
|
|
175
|
+
|
|
176
|
+
// The announcement key, not the text: progress events repeat the same state, and a
|
|
177
|
+
// live region that speaks on every byte is unusable. Only a real transition —
|
|
178
|
+
// started, finishing, done, failed, cancelled — changes what it says.
|
|
179
|
+
let announced = null;
|
|
180
|
+
let lastError = null;
|
|
181
|
+
|
|
182
|
+
function announce(text) {
|
|
183
|
+
if (text === announced) return;
|
|
184
|
+
announced = text;
|
|
185
|
+
statusEl.textContent = text;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function draw(snapshot) {
|
|
189
|
+
const { state } = snapshot;
|
|
190
|
+
const uploading = state === "uploading";
|
|
191
|
+
const percent = percentOf(snapshot);
|
|
192
|
+
const finishing = uploading && percent !== null && percent >= 100;
|
|
193
|
+
|
|
194
|
+
root.setAttribute("data-clay-upload-state", state);
|
|
195
|
+
|
|
196
|
+
if (uploading) {
|
|
197
|
+
// A retry draws a row that was showing a failure a moment ago: the progress
|
|
198
|
+
// element comes back, and the old error must not colour the retry button.
|
|
199
|
+
progressEl.hidden = false;
|
|
200
|
+
lastError = null;
|
|
201
|
+
if (percent === null) {
|
|
202
|
+
progressEl.removeAttribute("value");
|
|
203
|
+
progressEl.removeAttribute("max");
|
|
204
|
+
labelEl.textContent = "Uploading";
|
|
205
|
+
} else {
|
|
206
|
+
progressEl.setAttribute("max", "100");
|
|
207
|
+
progressEl.setAttribute("value", String(percent));
|
|
208
|
+
labelEl.textContent = finishing ? "Finishing upload" : `${percent}%`;
|
|
209
|
+
}
|
|
210
|
+
announce(finishing ? `Finishing upload of ${name}` : `Uploading ${name}`);
|
|
211
|
+
} else {
|
|
212
|
+
const message = snapshot.result?.msg || (state === "succeeded" ? "Uploaded" : state === "cancelled" ? "Upload cancelled" : "Upload failed");
|
|
213
|
+
if (state === "succeeded") {
|
|
214
|
+
progressEl.setAttribute("max", "100");
|
|
215
|
+
progressEl.setAttribute("value", "100");
|
|
216
|
+
} else {
|
|
217
|
+
progressEl.removeAttribute("value");
|
|
218
|
+
progressEl.removeAttribute("max");
|
|
219
|
+
}
|
|
220
|
+
progressEl.hidden = state !== "succeeded";
|
|
221
|
+
labelEl.textContent = message;
|
|
222
|
+
if (state === "failed") {
|
|
223
|
+
lastError = message;
|
|
224
|
+
announce(message);
|
|
225
|
+
} else if (state === "cancelled") {
|
|
226
|
+
lastError = null;
|
|
227
|
+
announce("Upload cancelled");
|
|
228
|
+
} else {
|
|
229
|
+
announce(message);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
cancelEl.hidden = !uploading;
|
|
234
|
+
// Dismissing a running upload would hide it while it still finishes and lands; Cancel is the way out then.
|
|
235
|
+
dismissEl.hidden = uploading;
|
|
236
|
+
retryEl.hidden = !canRetry(snapshot);
|
|
237
|
+
if (!retryEl.hidden) retryEl.setAttribute("aria-label", lastError ? `Retry ${name}: ${lastError}` : `Retry ${name}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function unmount() {
|
|
241
|
+
unsubscribe();
|
|
242
|
+
root.remove();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
cancelEl.addEventListener("click", () => {
|
|
246
|
+
if (cancelEl.hidden) return;
|
|
247
|
+
task.cancel();
|
|
248
|
+
});
|
|
249
|
+
retryEl.addEventListener("click", () => {
|
|
250
|
+
if (retryEl.hidden) return;
|
|
251
|
+
retryEl.hidden = true;
|
|
252
|
+
task.retry();
|
|
253
|
+
});
|
|
254
|
+
dismissEl.addEventListener("click", () => {
|
|
255
|
+
if (dismissEl.hidden) return;
|
|
256
|
+
unmount();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const unsubscribe = task.subscribe(draw);
|
|
260
|
+
host.append(root);
|
|
261
|
+
|
|
262
|
+
return { unmount };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export default mount;
|
package/src/plugins/upload.js
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
* const result = await clay.upload(file, { onProgress, signal });
|
|
11
11
|
* // { ok, msg, msgType, code, uploads }
|
|
12
12
|
*
|
|
13
|
+
* const task = clay.upload.start(file, { requiredAccess: "document-or-local" });
|
|
14
|
+
* const off = task.subscribe(({ state, percent }) => draw(state, percent));
|
|
15
|
+
* await task.result;
|
|
16
|
+
* task.retry();
|
|
17
|
+
* task.dispose();
|
|
18
|
+
*
|
|
13
19
|
* The result NEVER rejects, matching the save lane. One shape from every exit,
|
|
14
20
|
* because two channels for three outcomes (stored, cannot store here, failed) is
|
|
15
21
|
* the exact pattern save-core.js documents as the source of its own past bugs: a
|
|
@@ -18,27 +24,20 @@
|
|
|
18
24
|
* embedded.
|
|
19
25
|
*
|
|
20
26
|
* Codes a caller branches on:
|
|
21
|
-
* unsupported
|
|
22
|
-
* payment-required
|
|
23
|
-
* too-large
|
|
24
|
-
* unsupported-type
|
|
25
|
-
* timeout
|
|
27
|
+
* unsupported this host does not store files; embedding is correct
|
|
28
|
+
* payment-required the host stores files, but not for this account
|
|
29
|
+
* too-large over the host's cap, refused here or by the host
|
|
30
|
+
* unsupported-type the host will not store this kind of file
|
|
31
|
+
* timeout no answer; the upload may or may not have landed
|
|
32
|
+
* privacy-unsupported the host stored (or would store) the file where others can
|
|
33
|
+
* read it, so a URL from it must not be inserted anywhere
|
|
34
|
+
*
|
|
35
|
+
* The single-shot call and the task are the same transport and the same checks; the
|
|
36
|
+
* task adds an identity so progress, cancel and an explicit retry belong to one
|
|
37
|
+
* upload. `upload-task.js` holds both.
|
|
26
38
|
*/
|
|
27
39
|
|
|
28
|
-
import {
|
|
29
|
-
import { saveToken } from "../core/host-attrs.js";
|
|
30
|
-
|
|
31
|
-
const UPLOAD_PATH = "/_/upload";
|
|
32
|
-
const UPLOAD_TIMEOUT_MS = 120000;
|
|
33
|
-
|
|
34
|
-
function result(ok, msg, msgType, code, uploads = []) {
|
|
35
|
-
return { ok, msg, msgType, code, uploads };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function emit(name, detail) {
|
|
39
|
-
if (typeof document === "undefined") return;
|
|
40
|
-
document.dispatchEvent(new CustomEvent(`clay:upload-${name}`, { detail }));
|
|
41
|
-
}
|
|
40
|
+
import { checkRequiredAccess, runUpload, start, uploadParticipant } from "./upload-task.js";
|
|
42
41
|
|
|
43
42
|
/**
|
|
44
43
|
* Store one file on the host.
|
|
@@ -47,145 +46,27 @@ function emit(name, detail) {
|
|
|
47
46
|
* @param {Object} [options]
|
|
48
47
|
* @param {function} [options.onProgress] - ({loaded, total, percent})
|
|
49
48
|
* @param {AbortSignal} [options.signal]
|
|
49
|
+
* @param {?string} [options.requiredAccess] - "document", "local" or "document-or-local":
|
|
50
|
+
* the access the host must promise before the bytes are sent and report afterwards
|
|
50
51
|
* @returns {Promise<{ok: boolean, msg: string, msgType: string, code: ?string, uploads: Array}>}
|
|
51
52
|
*/
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// core host, and embedding is what it should do.
|
|
59
|
-
return result(false, "This host does not store uploaded files", "skipped", "unsupported");
|
|
60
|
-
}
|
|
61
|
-
// The host stores files, but not for this caller on this document: the spec withholds the
|
|
62
|
-
// document's upload block by omission, and a client that finds none does not upload. Not
|
|
63
|
-
// "unsupported", because embedding instead would route around the host's own decision.
|
|
64
|
-
if (!meta.document?.upload) {
|
|
65
|
-
return result(false, "You can't add files to this document", "error", "forbidden");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// The one local pre-check the spec asks for, and the only reason `maxBytes` is
|
|
69
|
-
// published: refusing a 40 MB photo here costs nothing, and sending it to be
|
|
70
|
-
// refused costs the person the whole upload first. Deliberately the ONLY thing
|
|
71
|
-
// decided locally. `document.upload.allowed` is not read as a veto: it can be
|
|
72
|
-
// stale (a plan upgraded in another tab, a share link granted since load), and
|
|
73
|
-
// the host answers the real reason with its own code on the request itself.
|
|
74
|
-
const cap = meta.document?.upload?.maxBytes;
|
|
75
|
-
if (typeof cap === "number" && cap > 0 && file.size > cap) {
|
|
76
|
-
return result(false, `That file is larger than this host accepts (${cap} bytes)`, "error", "too-large");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return send(file, { onProgress, signal });
|
|
53
|
+
export function upload(file, { requiredAccess, ...rest } = {}) {
|
|
54
|
+
// A misspelt requirement is the caller's bug, not the host's answer, and it throws
|
|
55
|
+
// here — before a request exists — rather than resolving as a failure the caller
|
|
56
|
+
// might read as "the host refused" and embed around.
|
|
57
|
+
checkRequiredAccess(requiredAccess);
|
|
58
|
+
return runUpload(file, { ...rest, requiredAccess });
|
|
80
59
|
}
|
|
81
60
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const path = token ? `${UPLOAD_PATH}/${token}` : UPLOAD_PATH;
|
|
86
|
-
// Absolute against the real origin, never left relative: a <base href> in the
|
|
87
|
-
// authored document would otherwise redirect this request, and the
|
|
88
|
-
// per-document token in its path, to an origin the document chose.
|
|
89
|
-
const url = new URL(path, window.location.origin).href;
|
|
90
|
-
|
|
91
|
-
// XMLHttpRequest for one reason: fetch cannot report upload progress. A photo
|
|
92
|
-
// over a phone connection is the case this whole capability is for, and a
|
|
93
|
-
// picker that sits there saying nothing for twenty seconds reads as broken.
|
|
94
|
-
const xhr = new XMLHttpRequest();
|
|
95
|
-
let settled = false;
|
|
96
|
-
|
|
97
|
-
const finish = (value) => {
|
|
98
|
-
if (settled) return;
|
|
99
|
-
settled = true;
|
|
100
|
-
clearTimeout(timeoutId);
|
|
101
|
-
if (signal) signal.removeEventListener("abort", onAbort);
|
|
102
|
-
resolve(value);
|
|
103
|
-
};
|
|
61
|
+
// The shared row (upload-ui.js) loads on first use, so a document that never uploads
|
|
62
|
+
// never downloads it, and clay.upload(file) keeps working untouched.
|
|
63
|
+
upload.mount = async (task, container) => (await import("./upload-ui.js")).mount(task, container);
|
|
104
64
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const timeoutId = setTimeout(() => {
|
|
111
|
-
xhr.abort();
|
|
112
|
-
// A timeout is not evidence the upload failed. The bytes may well have
|
|
113
|
-
// landed, so this says "we do not know" rather than asserting a failure,
|
|
114
|
-
// and the content-hash name makes the retry idempotent if the caller resends.
|
|
115
|
-
fail("Upload timed out", "unknown", "timeout");
|
|
116
|
-
}, UPLOAD_TIMEOUT_MS);
|
|
117
|
-
|
|
118
|
-
const onAbort = () => {
|
|
119
|
-
xhr.abort();
|
|
120
|
-
finish(result(false, "Upload cancelled", "skipped", "aborted"));
|
|
121
|
-
};
|
|
122
|
-
if (signal) {
|
|
123
|
-
if (signal.aborted) return onAbort();
|
|
124
|
-
signal.addEventListener("abort", onAbort);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
xhr.upload.addEventListener("progress", (event) => {
|
|
128
|
-
if (!event.lengthComputable) return;
|
|
129
|
-
const detail = {
|
|
130
|
-
loaded: event.loaded,
|
|
131
|
-
total: event.total,
|
|
132
|
-
percent: Math.round((event.loaded / event.total) * 100)
|
|
133
|
-
};
|
|
134
|
-
emit("progress", detail);
|
|
135
|
-
if (onProgress) {
|
|
136
|
-
try { onProgress(detail); } catch (_) { /* a caller's drawing must not kill the upload */ }
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
xhr.addEventListener("load", () => {
|
|
141
|
-
let body = {};
|
|
142
|
-
try { body = JSON.parse(xhr.responseText || "{}"); } catch (_) { body = {}; }
|
|
143
|
-
|
|
144
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
145
|
-
const uploads = Array.isArray(body.uploads) ? body.uploads : [];
|
|
146
|
-
if (!uploads.length || typeof uploads[0]?.url !== "string") {
|
|
147
|
-
return fail("The host accepted the file but did not say where it put it", "error", "bad-response");
|
|
148
|
-
}
|
|
149
|
-
emit("done", { uploads });
|
|
150
|
-
return finish(result(true, body.msg ?? "Uploaded", body.msgType || "success", body.code ?? null, uploads));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// The host's own code when it sent one, so a caller branches on the reason
|
|
154
|
-
// rather than pattern-matching a message. Falling back to the status keeps
|
|
155
|
-
// a host that answers a bare 413 readable.
|
|
156
|
-
const code = body.code || STATUS_CODES[xhr.status] || "error";
|
|
157
|
-
return fail(body.msg || `Upload failed (${xhr.status})`, "error", code);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
xhr.addEventListener("error", () => fail("Upload failed", "error", "network"));
|
|
161
|
-
xhr.addEventListener("abort", () => finish(result(false, "Upload cancelled", "skipped", "aborted")));
|
|
162
|
-
|
|
163
|
-
xhr.open("POST", url, true);
|
|
164
|
-
// The exact equivalent of the save lane's `credentials: 'omit'` in the case
|
|
165
|
-
// where the two differ: a sandboxed document is cross-origin to its own host,
|
|
166
|
-
// and asking for credentials there needs Access-Control-Allow-Credentials
|
|
167
|
-
// back, which a token-minting host must never send.
|
|
168
|
-
xhr.withCredentials = false;
|
|
169
|
-
xhr.setRequestHeader("Document-URL", window.location.href);
|
|
170
|
-
|
|
171
|
-
// FormData, so the file streams as bytes. No FileReader and no base64: a
|
|
172
|
-
// whole-file string costs a third more bytes and holds the entire file in
|
|
173
|
-
// memory twice.
|
|
174
|
-
const form = new FormData();
|
|
175
|
-
form.append("file", file, file.name || "upload");
|
|
176
|
-
|
|
177
|
-
emit("start", { name: file.name || null, size: file.size ?? null });
|
|
178
|
-
xhr.send(form);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
65
|
+
// On the same function, so anything holding `clay.upload` has both and every existing
|
|
66
|
+
// `clay.upload(file)` call keeps working untouched.
|
|
67
|
+
upload.start = start;
|
|
181
68
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
413: "too-large",
|
|
185
|
-
415: "unsupported-type",
|
|
186
|
-
401: "unauthorized",
|
|
187
|
-
403: "forbidden",
|
|
188
|
-
404: "not-found"
|
|
189
|
-
};
|
|
69
|
+
// The factory lives with the transport it gates, and this is the path a plan resolves.
|
|
70
|
+
export { uploadParticipant as modeParticipant };
|
|
190
71
|
|
|
191
72
|
export default upload;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const DOWNLOAD_URL = "/_/download?as=htmlclay";
|
|
2
|
+
|
|
3
|
+
export function editOriginal() {
|
|
4
|
+
const url = new URL(window.location.href);
|
|
5
|
+
url.searchParams.set("editmode", "true");
|
|
6
|
+
window.location.href = url.toString();
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function backToDraft() {
|
|
11
|
+
const url = new URL(window.location.href);
|
|
12
|
+
url.searchParams.delete("editmode");
|
|
13
|
+
window.location.href = url.toString();
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function inertVisitorDrafts() {
|
|
18
|
+
return {
|
|
19
|
+
active: false,
|
|
20
|
+
slot: null,
|
|
21
|
+
key: null,
|
|
22
|
+
hasDraft: () => false,
|
|
23
|
+
restored: () => null,
|
|
24
|
+
saveNow: () => false,
|
|
25
|
+
reset: () => false,
|
|
26
|
+
onRestore: () => false,
|
|
27
|
+
editOriginal,
|
|
28
|
+
backToDraft,
|
|
29
|
+
downloadUrl: () => DOWNLOAD_URL,
|
|
30
|
+
source: null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { INTRO_CSS } from "./intro-style.js";
|
|
2
2
|
import { announceBottomUi } from "./bottom-ui.js";
|
|
3
|
+
import { bootPhase } from "../../core/boot-phase.js";
|
|
3
4
|
|
|
4
5
|
export const COPY = {
|
|
5
6
|
close: "Hide this introduction",
|
|
@@ -45,7 +46,7 @@ export function mountIntro({ memory, template, appName, hadDraft }) {
|
|
|
45
46
|
if (!template || !template.content.textContent.trim() && !template.content.querySelector("img, svg, video, canvas")) return null;
|
|
46
47
|
if (!forced()) {
|
|
47
48
|
if (memory.get().intro.done || hadDraft) return null;
|
|
48
|
-
if (
|
|
49
|
+
if (bootPhase.servedHash) return null;
|
|
49
50
|
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
50
51
|
}
|
|
51
52
|
return mount({ memory, template, appName });
|
|
@@ -173,6 +173,9 @@ export function mountOwnerChip() {
|
|
|
173
173
|
document.addEventListener("pointerup", lift, true);
|
|
174
174
|
window.addEventListener("hashchange", lift);
|
|
175
175
|
lift();
|
|
176
|
+
// Whoever removes the chip takes its listeners back with it: a chip mounted and unmounted
|
|
177
|
+
// once per mode session would otherwise leave six behind each time.
|
|
178
|
+
chip.liftOff = liftOff;
|
|
176
179
|
|
|
177
180
|
return chip;
|
|
178
181
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PROMPT_CSS } from "./prompt-style.js";
|
|
2
1
|
import { announceBottomUi, bottomBarHeight } from "./bottom-ui.js";
|
|
3
2
|
|
|
4
3
|
const FONT_HREF = "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500&family=IBM+Plex+Sans:wght@400;500;600&family=Newsreader:opsz,wght@6..72,500&display=swap";
|
|
@@ -24,8 +23,9 @@ const ASIDE = `<aside class="hcp" id="hcp" hidden aria-label="Download this app"
|
|
|
24
23
|
</div>
|
|
25
24
|
</aside>`;
|
|
26
25
|
|
|
27
|
-
export function mountPrompt({ memory, appName, link }) {
|
|
26
|
+
export async function mountPrompt({ memory, appName, link }) {
|
|
28
27
|
if (!document.getElementById("hcp-style")) {
|
|
28
|
+
const { PROMPT_CSS } = await import("./prompt-style.js");
|
|
29
29
|
document.head.insertAdjacentHTML("beforeend", `<link rel="stylesheet" clay="editor-ui" href="${FONT_HREF}">\n<style clay="editor-ui" id="hcp-style">${PROMPT_CSS}</style>`);
|
|
30
30
|
}
|
|
31
31
|
if (!document.getElementById("hcp")) document.body.insertAdjacentHTML("beforeend", ASIDE);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isEditMode, isOwner } from "../core/is-edit-mode.js";
|
|
2
|
+
import { hasSaveToken } from "../core/host-attrs.js";
|
|
3
|
+
import { draftSlot } from "../core/draft-mode.js";
|
|
4
|
+
import { inertVisitorDrafts } from "./visitor-drafts/inert.js";
|
|
5
|
+
|
|
6
|
+
const slot = draftSlot();
|
|
7
|
+
const rulesTag = document.querySelector('script[data-rules-name~="api"]');
|
|
8
|
+
|
|
9
|
+
// The one chip, the mounting on its way to it, and the session that asked: the boot and a
|
|
10
|
+
// mode lease both ask, so a second ask joins the first, and a chip that arrives for a
|
|
11
|
+
// session that has ended is taken down at once.
|
|
12
|
+
let mounted = null;
|
|
13
|
+
let mounting = null;
|
|
14
|
+
let sessions = 0;
|
|
15
|
+
|
|
16
|
+
const chipAllowed = () => isOwner && isEditMode && !hasSaveToken() && !!slot && !!rulesTag;
|
|
17
|
+
|
|
18
|
+
function mountChip() {
|
|
19
|
+
if (mounted) return Promise.resolve(mounted);
|
|
20
|
+
if (!mounting) {
|
|
21
|
+
const asked = sessions;
|
|
22
|
+
mounting = import("./visitor-drafts/owner-chip.js")
|
|
23
|
+
.then(({ mountOwnerChip }) => {
|
|
24
|
+
const chip = mountOwnerChip() || document.getElementById("clay-owner");
|
|
25
|
+
if (asked === sessions) {
|
|
26
|
+
mounted = chip;
|
|
27
|
+
return chip;
|
|
28
|
+
}
|
|
29
|
+
chip?.liftOff?.();
|
|
30
|
+
chip?.remove();
|
|
31
|
+
return null;
|
|
32
|
+
})
|
|
33
|
+
.finally(() => { mounting = null; });
|
|
34
|
+
}
|
|
35
|
+
return mounting;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function unmountChip() {
|
|
39
|
+
sessions += 1;
|
|
40
|
+
mounted?.liftOff?.();
|
|
41
|
+
mounted?.remove();
|
|
42
|
+
mounted = null;
|
|
43
|
+
document.getElementById("clay-owner-vars")?.remove();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!slot || !rulesTag) {
|
|
47
|
+
const missing = [];
|
|
48
|
+
if (!slot) missing.push('a visitor-drafts="<slot>" attribute on <html>');
|
|
49
|
+
if (!rulesTag) missing.push('a <script data-rules-name~="api"> rules tag');
|
|
50
|
+
console.warn(`visitor-drafts: this page keeps no draft (missing ${missing.join(" and ")})`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ownerChip = chipAllowed() ? mountChip() : Promise.resolve(null);
|
|
54
|
+
|
|
55
|
+
export const ready = ownerChip;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The owner chip as a mode lease: `enable` mounts it, `initial: true` adopts the one the
|
|
59
|
+
* boot armed and mounts nothing, and `dispose` (after the barrier) takes the chip and the
|
|
60
|
+
* room it published down. The chip holds no input anybody has committed, so `quiesce` and
|
|
61
|
+
* `resume` leave it exactly where it is.
|
|
62
|
+
*/
|
|
63
|
+
export function draftsOwnerParticipant() {
|
|
64
|
+
return {
|
|
65
|
+
id: "clay:drafts-owner",
|
|
66
|
+
order: 400,
|
|
67
|
+
prepare(context = {}) {
|
|
68
|
+
return {
|
|
69
|
+
enable() {
|
|
70
|
+
if (context.initial === true) return;
|
|
71
|
+
if (chipAllowed()) mountChip();
|
|
72
|
+
},
|
|
73
|
+
quiesce() {},
|
|
74
|
+
resume() {},
|
|
75
|
+
dispose() {
|
|
76
|
+
unmountChip();
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { draftsOwnerParticipant as modeParticipant };
|
|
84
|
+
|
|
85
|
+
export const visitorDrafts = inertVisitorDrafts();
|