@panphora/clayjs 1.16.0 → 1.18.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/dist/all.js +1 -1
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +72519 -59839
- package/dist/m/attrs/refetch-on-save-ZUE7PSCI.js +1 -0
- package/dist/m/attrs/save-freeze-LWGF6XUN.js +1 -0
- package/dist/m/c-2GRUM77Q.js +30 -0
- package/dist/m/c-2KIEGNC4.js +1 -0
- package/dist/m/{c-5R4PTRG2.js → c-2KL3BD52.js} +1 -1
- package/dist/m/c-2YYDMLNZ.js +1 -0
- package/dist/m/c-2ZZBC45O.js +1 -0
- package/dist/m/c-372N6H3B.js +1 -0
- package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
- package/dist/m/c-3LDGIBMN.js +1 -0
- package/dist/m/c-3O7OBDCM.js +1 -0
- package/dist/m/c-4ASQPRQL.js +1 -0
- package/dist/m/c-4TXNLX4K.js +6 -0
- package/dist/m/c-4YU3BR4D.js +6 -0
- package/dist/m/c-5734MF2P.js +1 -0
- package/dist/m/c-57XDY6HC.js +1 -0
- package/dist/m/{c-VFX45IZQ.js → c-5B4YBZXC.js} +1 -1
- package/dist/m/c-5ONTYIUI.js +1 -0
- package/dist/m/c-5OOVFCJM.js +1 -0
- package/dist/m/c-5T6NZHNM.js +1 -0
- package/dist/m/{c-YDH7Q3NY.js → c-5ZQV7KC4.js} +1 -1
- package/dist/m/{c-Z36J63PP.js → c-6DB2OLRJ.js} +1 -1
- package/dist/m/c-6DNRCPNM.js +1 -0
- package/dist/m/c-6UGWFMQC.js +2 -0
- package/dist/m/c-7K4SCQB7.js +1 -0
- package/dist/m/c-7MG5RU63.js +1 -0
- package/dist/m/c-7PRJCA3E.js +1 -0
- package/dist/m/c-A6L3FMUS.js +1 -0
- package/dist/m/c-AMU3BPPR.js +4 -0
- package/dist/m/c-AMYEFA2E.js +1 -0
- package/dist/m/c-AQYCG7QW.js +1 -0
- package/dist/m/c-ATKAOH23.js +1 -0
- package/dist/m/c-B3JM5ZXC.js +1 -0
- package/dist/m/c-B5CS55WR.js +1 -0
- package/dist/m/c-B5UJQT7W.js +1 -0
- package/dist/m/c-B7J4K2AE.js +3 -0
- package/dist/m/c-BHZYSHW4.js +1 -0
- package/dist/m/c-BIJALSRD.js +1 -0
- package/dist/m/c-BJ7YQ5QZ.js +1 -0
- package/dist/m/c-BPF2NSPV.js +1 -0
- package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
- package/dist/m/c-BUAYFEV5.js +1 -0
- package/dist/m/c-C6IPRG34.js +1 -0
- package/dist/m/{c-2JL6E75N.js → c-CAS3NND4.js} +1 -1
- package/dist/m/{c-4DRMS4QG.js → c-CCQ25GFY.js} +1 -1
- package/dist/m/c-CJFDLD43.js +1 -0
- package/dist/m/c-COPTL5WB.js +1 -0
- package/dist/m/c-D3E4WJ4M.js +166 -0
- package/dist/m/c-D5IKEKXW.js +7 -0
- package/dist/m/c-DHBRA6DL.js +1 -0
- package/dist/m/c-DKLS74XB.js +72 -0
- package/dist/m/c-DVYGFTSK.js +3 -0
- package/dist/m/c-E3ZXARWA.js +1 -0
- package/dist/m/c-EK63G3CG.js +1 -0
- package/dist/m/c-ERQSNR6K.js +13 -0
- package/dist/m/c-EVYAISP3.js +1 -0
- package/dist/m/c-EXKHPOVC.js +3 -0
- package/dist/m/{c-7VGZ7RU5.js → c-FWKHM4E7.js} +1 -1
- package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
- package/dist/m/c-GNDL3TKB.js +1 -0
- package/dist/m/c-GRWMD6O5.js +1 -0
- package/dist/m/c-GRXHHFPO.js +1 -0
- package/dist/m/{c-OYTFP5QN.js → c-GY422N3Q.js} +1 -1
- package/dist/m/c-HAWFNBYG.js +1 -0
- package/dist/m/c-HG2C77BM.js +4 -0
- package/dist/m/c-HHGJBU4D.js +1 -0
- package/dist/m/c-HMV6P4RX.js +1 -0
- package/dist/m/c-HMYWIHFG.js +1 -0
- package/dist/m/c-HT2DKBV2.js +1 -0
- package/dist/m/c-I2SCCUEW.js +1 -0
- package/dist/m/{c-HRDCD3WN.js → c-ICJYHABF.js} +2 -2
- package/dist/m/c-IMZCWYHR.js +7 -0
- package/dist/m/c-IVMD3FEM.js +1 -0
- package/dist/m/c-IVTRVENZ.js +1 -0
- package/dist/m/c-IW6GGOU2.js +1 -0
- package/dist/m/c-JKZJIUIP.js +1 -0
- package/dist/m/{c-NZMNXVF6.js → c-JNLX3RJB.js} +1 -1
- package/dist/m/c-JRKY44DK.js +1 -0
- package/dist/m/c-K3ADYNLU.js +1 -0
- package/dist/m/c-KBS3UV6B.js +1 -0
- package/dist/m/c-KGHVFRR3.js +1 -0
- package/dist/m/c-KJJPV77R.js +1 -0
- package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
- package/dist/m/{c-5Y6TNX5W.js → c-KP64LT2A.js} +2 -2
- package/dist/m/c-KQ7E3PYY.js +1 -0
- package/dist/m/c-L3F5ZLIV.js +1 -0
- package/dist/m/{c-LR4CGEPT.js → c-L42OLXWM.js} +3 -2
- package/dist/m/c-L5VFATT6.js +1 -0
- package/dist/m/c-L7JL5TL3.js +3 -0
- package/dist/m/c-LKSL4AUJ.js +1 -0
- package/dist/m/c-M6FNQCTQ.js +1 -0
- package/dist/m/c-MCOOHCSG.js +1 -0
- package/dist/m/c-MHI3GYKF.js +1 -0
- package/dist/m/c-MWXFLW4J.js +1 -0
- package/dist/m/c-NEL7HQW3.js +1 -0
- package/dist/m/c-NQJRVJ56.js +1 -0
- package/dist/m/c-NWX3MFT4.js +1 -0
- package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
- package/dist/m/c-OKXI52N7.js +1 -0
- package/dist/m/{c-MMUIUUZG.js → c-OPLUZR3A.js} +1 -1
- package/dist/m/{c-J7CA54IP.js → c-OTMKN2OO.js} +1 -1
- package/dist/m/{c-LTGMXXKO.js → c-OYRZ7XG2.js} +1 -1
- package/dist/m/c-P3OFYUMA.js +1 -0
- package/dist/m/c-PLN76GF3.js +6 -0
- package/dist/m/c-PLP4JKFC.js +1 -0
- package/dist/m/c-Q26MME6N.js +1 -0
- package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
- package/dist/m/{c-SWRJFCNN.js → c-QIKT6SKM.js} +1 -1
- package/dist/m/c-QIXT3Q7C.js +28 -0
- package/dist/m/c-QO4HNGCQ.js +1 -0
- package/dist/m/c-R27DWQKX.js +1 -0
- package/dist/m/{c-HVYXIS72.js → c-R2XUODVY.js} +1 -1
- package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
- package/dist/m/c-RND3G2S2.js +1 -0
- package/dist/m/c-S2U6NJU4.js +1 -0
- package/dist/m/c-SPZPGD3R.js +1 -0
- package/dist/m/c-SW4YEG2D.js +8 -0
- package/dist/m/c-SZGYW5HT.js +1 -0
- package/dist/m/c-T637T7SN.js +1 -0
- package/dist/m/c-TJAXGIRV.js +1 -0
- package/dist/m/{c-KKWLDOPM.js → c-TNEM3RNM.js} +1 -1
- package/dist/m/c-TTB5EK4S.js +32 -0
- package/dist/m/c-U7SHNWDH.js +1 -0
- package/dist/m/c-UDHIVLYH.js +20 -0
- package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
- package/dist/m/{c-TQZ2JYHD.js → c-UORXLSD3.js} +1 -1
- package/dist/m/c-UPD33ZPE.js +1 -0
- package/dist/m/c-URSMSFCB.js +1 -0
- package/dist/m/c-UY4QX42B.js +1 -0
- package/dist/m/c-V44KWI2L.js +4 -0
- package/dist/m/c-VG5XIN4T.js +1 -0
- package/dist/m/c-VILENZTD.js +1 -0
- package/dist/m/{c-33HUYEDI.js → c-VJYEVRYK.js} +1 -1
- package/dist/m/c-VR2BJ4AN.js +1 -0
- package/dist/m/c-W4JCVLAB.js +1 -0
- package/dist/m/c-W6DQSHXI.js +135 -0
- package/dist/m/c-WBGWGOM7.js +1 -0
- package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
- package/dist/m/c-WGW764WX.js +1 -0
- package/dist/m/c-WMRJKQ2V.js +5 -0
- package/dist/m/c-WNTYKCDG.js +1 -0
- package/dist/m/c-WQWIRWO3.js +72 -0
- package/dist/m/c-X5LNHET5.js +1 -0
- package/dist/m/c-X5TSXZYJ.js +2 -0
- package/dist/m/c-XGH4GS2T.js +1 -0
- package/dist/m/c-Y6F2FORL.js +1 -0
- package/dist/m/c-YBKHUAK7.js +10 -0
- package/dist/m/c-YBSS55KU.js +20 -0
- package/dist/m/c-YCLU4UNT.js +2 -0
- package/dist/m/c-YVZ2L7CE.js +1 -0
- package/dist/m/c-ZK4GIZH2.js +1 -0
- package/dist/m/c-ZRXJD4G6.js +1 -0
- package/dist/m/c-ZWBI4HYK.js +1 -0
- package/dist/m/{c-HT33EQRN.js → c-ZWIOJGMT.js} +1 -1
- package/dist/m/core/admin-attrs-YY7T5FGU.js +1 -0
- package/dist/m/core/autosave-S6OMNDCK.js +1 -0
- package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
- package/dist/m/core/host-permission-6EVJGNPL.js +1 -0
- package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
- package/dist/m/core/{persist-HKHA66IY.js → persist-UKXV5KLA.js} +1 -1
- package/dist/m/core/recovery-DRFZRJED.js +1 -0
- package/dist/m/core/save-LYY7K6U7.js +1 -0
- package/dist/m/core/save-core-MKRPSEEO.js +1 -0
- package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
- package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
- package/dist/m/events/index-RN2JNGBV.js +1 -0
- package/dist/m/internals/index-TGLQINN3.js +1 -0
- package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
- package/dist/m/loader-3BBNZZGM.js +1 -0
- package/dist/m/manifest-F449FF98.js +1 -0
- package/dist/m/manifest-edit-4396A009.js +1 -0
- package/dist/m/manifest.json +2509 -1474
- package/dist/m/options/options-EJMS3K24.js +1 -0
- package/dist/m/page/editor-6LRB3GE4.js +304 -0
- package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
- package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
- package/dist/m/plugins/demo-OYO5GCAO.js +1 -0
- package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
- package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
- package/dist/m/plugins/live-AD4H6XSL.js +1 -0
- package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
- package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
- package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
- package/dist/m/plugins/people-JCHVPF7P.js +1 -0
- package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
- package/dist/m/plugins/{richclay-RC3L2F6X.js → richclay-KLGETK77.js} +2 -2
- package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
- package/dist/m/plugins/source-TJXOIOYY.js +1 -0
- package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
- package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
- package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
- package/dist/m/plugins/upload-NHGJXZK2.js +1 -0
- package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
- package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
- package/dist/m/plugins/wire-VBWXEXU2.js +1 -0
- package/dist/m/reader-core-4W2KYEWQ.js +77 -0
- package/dist/m/route/route-62CHXL5B.js +1 -0
- package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
- package/dist/m/ui/index-IVWN2QC4.js +1 -0
- package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
- package/dist/offline-manifest.json +1560 -889
- package/dist/page/themes/light-1.css +33 -8
- package/dist/server/core.cjs +1554 -311
- package/dist/server/offline-runtime.cjs +63 -63
- package/dist/server/offline-runtime.mjs +63 -63
- package/dist/server/page-compaction-worker.cjs +3710 -0
- package/dist/server/rooms.mjs +5 -0
- package/docs/guide.md +653 -101
- package/entries/sap.js +5 -5
- package/package.json +1 -1
- package/src/core/admin-contenteditable.js +5 -2
- package/src/core/admin-inputs.js +1 -0
- package/src/core/admin-onclick.js +2 -1
- package/src/core/boot-phase.js +2 -1
- package/src/core/conflict-download.js +3 -29
- package/src/core/conflict-notice-ui.js +4 -7
- package/src/core/download.js +32 -0
- package/src/core/host-meta.js +56 -0
- package/src/core/is-edit-mode.js +17 -1
- package/src/core/mode-key.js +125 -19
- package/src/core/mode-support.js +5 -0
- package/src/core/mode-transition.js +20 -6
- package/src/core/preserve-authored.js +4 -4
- package/src/core/recovery.js +121 -33
- package/src/core/save-accepted.js +33 -0
- package/src/core/save-core.js +111 -32
- package/src/core/save-job.js +108 -0
- package/src/core/save-prepare.js +53 -0
- package/src/core/save-size-notice.js +3 -1
- package/src/core/save-slices.js +15 -0
- package/src/core/save.js +557 -127
- package/src/core/serialize-steps.js +109 -0
- package/src/core/snapshot.js +209 -40
- package/src/core/source-map.js +83 -38
- package/src/core/stale-host-notice.js +1 -0
- package/src/core/upgrade-run.js +59 -8
- package/src/dom/active-element.js +5 -0
- package/src/lib/assets.js +1 -0
- package/src/lib/autosave-scheduler.js +3 -4
- package/src/lib/slice.js +65 -0
- package/src/lib/uimotion.js +774 -0
- package/src/lib/unsaved-state.js +2 -2
- package/src/live/fast-lane.js +10 -3
- package/src/live/local-lane.js +202 -11
- package/src/live/ops.js +16 -0
- package/src/live/page-compaction.js +162 -0
- package/src/live/server-lane.js +1 -0
- package/src/live/store.js +11 -3
- package/src/loader-logic.js +3 -1
- package/src/loader.js +14 -4
- package/src/page/adopt-boot.js +2 -1
- package/src/page/awareness.js +33 -2
- package/src/page/blocks.js +122 -27
- package/src/page/board-views.js +2 -16
- package/src/page/boot-pack.js +1 -1
- package/src/page/chart-tables.js +29 -0
- package/src/page/chart.js +411 -72
- package/src/page/checklist.js +10 -135
- package/src/page/checkpoint-proof.js +295 -0
- package/src/page/chrome.js +257 -40
- package/src/page/clipboard-shape.js +126 -0
- package/src/page/document-title-dom.js +12 -0
- package/src/page/document-title-runtime.js +15 -1
- package/src/page/document-title.js +4 -1
- package/src/page/drag.js +33 -3
- package/src/page/editor.js +207 -31
- package/src/page/emoji-data.js +162 -0
- package/src/page/export-assets.js +357 -0
- package/src/page/export-html.js +209 -0
- package/src/page/export-markdown.js +361 -0
- package/src/page/features/board.js +12 -3
- package/src/page/features/chart.js +11 -0
- package/src/page/features/checklist.js +428 -0
- package/src/page/features/dates.js +180 -0
- package/src/page/features/emoji.js +187 -0
- package/src/page/features/export-html.js +189 -0
- package/src/page/features/export-markdown.js +169 -0
- package/src/page/features/import-markdown.js +349 -0
- package/src/page/features/media.js +61 -5
- package/src/page/features/mentions.js +188 -0
- package/src/page/features/registry.js +24 -1
- package/src/page/features/table.js +375 -85
- package/src/page/features/title-cover.js +331 -0
- package/src/page/features/title-icon.js +213 -0
- package/src/page/features/toc.js +87 -0
- package/src/page/format-command.js +33 -0
- package/src/page/format-gate.js +68 -18
- package/src/page/format-host.js +71 -0
- package/src/page/format-native-commands.js +158 -0
- package/src/page/format-native.js +42 -0
- package/src/page/image-actions.js +64 -0
- package/src/page/import-markdown.js +262 -0
- package/src/page/inline.js +32 -8
- package/src/page/items.js +141 -11
- package/src/page/markdown.js +30 -2
- package/src/page/media.js +35 -119
- package/src/page/mention-picker.js +99 -0
- package/src/page/merge.js +1 -1
- package/src/page/placeholder.js +14 -1
- package/src/page/projection.js +156 -25
- package/src/page/recover.js +10 -12
- package/src/page/recovery-ui.js +464 -0
- package/src/page/rich/chart.js +63 -7
- package/src/page/save-projection.js +67 -0
- package/src/page/save-states.js +14 -4
- package/src/page/schema.js +52 -3
- package/src/page/table-paste.js +3 -1
- package/src/page/table-style.js +4 -4
- package/src/page/table.js +44 -2
- package/src/page/themes/content.css +25 -1
- package/src/page/themes/light-1.css +33 -8
- package/src/page/themes/light-1.source.css +8 -7
- package/src/page/toc-link.js +31 -0
- package/src/page/toc.js +5 -0
- package/src/page/view-hooks.js +58 -0
- package/src/page/view.js +13 -0
- package/src/page/ybind/binding.js +116 -6
- package/src/page/ybind/bridge.js +187 -20
- package/src/page/ybind/containment.js +162 -0
- package/src/page/ybind/doc.js +17 -9
- package/src/page/ybind/file-debt.js +63 -0
- package/src/page/ybind/layout.js +7 -2
- package/src/page/ybind/marks.js +2 -1
- package/src/page/ybind/moves.js +5 -4
- package/src/page/ybind/origins.js +106 -4
- package/src/page/ybind/parts.js +8 -1
- package/src/page/ybind/provider.js +125 -18
- package/src/page/ybind/recovery-proof.js +32 -9
- package/src/page/ybind/recovery-replay.js +905 -4
- package/src/page/ybind/recovery.js +174 -45
- package/src/page/ybind/table.js +67 -11
- package/src/page/ybind/walk-delta.js +258 -0
- package/src/page/ybind/walk.js +54 -29
- package/src/page/ybind/writer.js +1 -1
- package/src/plugins/ai-edit.js +13 -9
- package/src/plugins/indicator-chip.js +91 -0
- package/src/plugins/indicator.js +13 -80
- package/src/plugins/live.js +126 -15
- package/src/plugins/menu/actions.js +196 -0
- package/src/plugins/menu/people.js +279 -0
- package/src/plugins/menu/placement.js +121 -0
- package/src/plugins/menu/preferences.js +9 -0
- package/src/plugins/menu/seo.js +316 -397
- package/src/plugins/menu/style.js +171 -0
- package/src/plugins/menu/variants.js +296 -0
- package/src/plugins/menu.js +186 -183
- package/src/plugins/page-view.js +3 -2
- package/src/plugins/page.js +362 -43
- package/src/plugins/sortable.js +8 -1
- package/src/plugins/source.js +6 -0
- package/src/plugins/upgrade.js +51 -17
- package/src/plugins/upload-ui.js +12 -17
- package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
- package/src/plugins/visitor-drafts/intro-style.js +7 -2
- package/src/plugins/visitor-drafts/intro.js +7 -4
- package/src/plugins/visitor-drafts/owner-chip.js +8 -12
- package/src/plugins/visitor-drafts/prompt.js +11 -9
- package/src/plugins/visitor-drafts/source.js +4 -3
- package/src/plugins/visitor-drafts/store.js +138 -9
- package/src/plugins/visitor-drafts/ui-memory.js +2 -2
- package/src/plugins/visitor-drafts-owner.js +4 -6
- package/src/plugins/visitor-drafts.js +5 -3
- package/src/recovery/store.js +1 -1
- package/src/route/route.js +4 -0
- package/src/server/checkpoint/index.js +2 -2
- package/src/server/rooms/index.js +5 -1
- package/src/server/rooms/live-data-rooms.js +302 -50
- package/src/server/rooms/page-compaction-worker.js +12 -0
- package/src/server/rooms/page-compactor.js +92 -0
- package/src/server/rooms/page-room-conformance.js +461 -0
- package/src/source/parse-steps.js +94 -0
- package/src/source/session.js +233 -57
- package/src/sync/editing-sender.js +12 -0
- package/src/sync/live-sync-edit.js +22 -2
- package/src/sync/live-sync.js +150 -23
- package/src/sync/page-relay.js +52 -0
- package/src/sync/presence-store.js +44 -4
- package/src/sync/presence.js +6 -3
- package/src/sync/saved-stream.js +28 -14
- package/src/ui/bevel-controls.js +1 -0
- package/src/ui/bevel-dialog.js +5 -2
- package/src/ui/editor-ui.js +77 -0
- package/src/ui/page-colour.js +281 -0
- package/src/ui/toast.js +16 -7
- package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +9 -9
- package/src/vendor/parse5.vendor.js +2 -2
- package/src/vendor/sap-core.vendor.js +5 -5
- package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +0 -1
- package/dist/m/attrs/save-freeze-FUP4Q4X3.js +0 -1
- package/dist/m/c-2M73252O.js +0 -1
- package/dist/m/c-2SPVQSHC.js +0 -1
- package/dist/m/c-35NBGMZB.js +0 -1
- package/dist/m/c-3IQRECYS.js +0 -1
- package/dist/m/c-4B6H4G5V.js +0 -1
- package/dist/m/c-4FFHZM32.js +0 -1
- package/dist/m/c-4YSCRICO.js +0 -1
- package/dist/m/c-54RCDCBU.js +0 -1
- package/dist/m/c-5M4YEKCX.js +0 -4
- package/dist/m/c-5V3MOVJT.js +0 -1
- package/dist/m/c-66O32MV3.js +0 -28
- package/dist/m/c-6HMB24EV.js +0 -1
- package/dist/m/c-7O6AFR65.js +0 -1
- package/dist/m/c-ANQTJ3B5.js +0 -2
- package/dist/m/c-AYVOREBE.js +0 -1
- package/dist/m/c-B2ZXPOS3.js +0 -3
- package/dist/m/c-BMIZKMPV.js +0 -1
- package/dist/m/c-BTDI4Y23.js +0 -1
- package/dist/m/c-CJZSLMVQ.js +0 -1
- package/dist/m/c-DBE6ORSI.js +0 -1
- package/dist/m/c-EDHUR4MX.js +0 -1
- package/dist/m/c-EY6UOM54.js +0 -1
- package/dist/m/c-F3CKJTJS.js +0 -1
- package/dist/m/c-F7Y6FLPY.js +0 -1
- package/dist/m/c-FA4DZVTC.js +0 -1
- package/dist/m/c-G2BUZITR.js +0 -1
- package/dist/m/c-GB3JTV6E.js +0 -1
- package/dist/m/c-GBGWXTX4.js +0 -1
- package/dist/m/c-GHBCCVG4.js +0 -1
- package/dist/m/c-GJAEGGXY.js +0 -1
- package/dist/m/c-GOWBDM6J.js +0 -1
- package/dist/m/c-IB52R237.js +0 -1
- package/dist/m/c-IKWNE4R4.js +0 -1
- package/dist/m/c-IU3IYEU6.js +0 -1
- package/dist/m/c-IZ2EBRAJ.js +0 -1
- package/dist/m/c-KR4UW4KW.js +0 -1
- package/dist/m/c-KY3VJGUA.js +0 -8
- package/dist/m/c-LFPHCXY3.js +0 -1
- package/dist/m/c-LRPUO2MX.js +0 -1
- package/dist/m/c-LXENEBV7.js +0 -1
- package/dist/m/c-NMJRXKPC.js +0 -8
- package/dist/m/c-OIBUTDXI.js +0 -1
- package/dist/m/c-PPOSCAQD.js +0 -1
- package/dist/m/c-QIX5BJYN.js +0 -1
- package/dist/m/c-QQHEX6HA.js +0 -1
- package/dist/m/c-R7RMANJI.js +0 -9
- package/dist/m/c-RI4NT75Y.js +0 -1
- package/dist/m/c-RM7J7BV2.js +0 -1
- package/dist/m/c-RUJKSUQW.js +0 -1
- package/dist/m/c-SGCCC4FW.js +0 -25
- package/dist/m/c-T4VO4OCB.js +0 -1
- package/dist/m/c-TQEMVJCS.js +0 -1
- package/dist/m/c-TR6RMDQ7.js +0 -3
- package/dist/m/c-UKHTLWUW.js +0 -1
- package/dist/m/c-UNN7LWCT.js +0 -1
- package/dist/m/c-UXBMRDZB.js +0 -235
- package/dist/m/c-V7PZZRG7.js +0 -1
- package/dist/m/c-VL52ADDR.js +0 -1
- package/dist/m/c-WNYBLIOZ.js +0 -1
- package/dist/m/c-WWDBYXLK.js +0 -1
- package/dist/m/c-XNNW2CWK.js +0 -1
- package/dist/m/c-Y6USGTS5.js +0 -5
- package/dist/m/c-YG2NJYS6.js +0 -135
- package/dist/m/c-Z4VRC7GU.js +0 -7
- package/dist/m/c-Z6K52OLP.js +0 -19
- package/dist/m/c-ZDY2MDAA.js +0 -21
- package/dist/m/c-ZUCOKOHT.js +0 -1
- package/dist/m/core/admin-attrs-VGNLAUDK.js +0 -1
- package/dist/m/core/autosave-E4PEFPZ6.js +0 -1
- package/dist/m/core/conflict-notice-VQ6Y64PH.js +0 -1
- package/dist/m/core/host-permission-XN3K2IW3.js +0 -1
- package/dist/m/core/is-edit-mode-JXHDVGUL.js +0 -1
- package/dist/m/core/recovery-TBBSICE2.js +0 -1
- package/dist/m/core/save-WUCHTQBE.js +0 -1
- package/dist/m/core/save-core-FJVHT5M3.js +0 -1
- package/dist/m/core/snapshot-F3SWDZMM.js +0 -1
- package/dist/m/core/unsaved-warning-STKBHE6G.js +0 -1
- package/dist/m/events/index-23DPTUT4.js +0 -1
- package/dist/m/internals/index-WOVAP3RU.js +0 -1
- package/dist/m/lib/cache-bust-CGWT6ZJI.js +0 -1
- package/dist/m/loader-HXX6YNH5.js +0 -1
- package/dist/m/manifest-F22DEDAC.js +0 -1
- package/dist/m/manifest-edit-C3C7376B.js +0 -1
- package/dist/m/options/options-2LH76JQ3.js +0 -1
- package/dist/m/page/editor-XUTB2HB2.js +0 -189
- package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +0 -6
- package/dist/m/plugins/demo-ZIBFAA3R.js +0 -1
- package/dist/m/plugins/indicator-PV4YW2ID.js +0 -1
- package/dist/m/plugins/live-NUTTXSBS.js +0 -1
- package/dist/m/plugins/menu-HCKO3DSP.js +0 -138
- package/dist/m/plugins/page-KSUH7G6J.js +0 -2
- package/dist/m/plugins/page-view-W73OW7WI.js +0 -1
- package/dist/m/plugins/people-AJJ32YCG.js +0 -1
- package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
- package/dist/m/plugins/sortable-YRMQX3ZU.js +0 -1
- package/dist/m/plugins/source-KHK5UUSK.js +0 -1
- package/dist/m/plugins/undo-OF2L3CXU.js +0 -1
- package/dist/m/plugins/upgrade-ZNYODP5Y.js +0 -1
- package/dist/m/plugins/upload-XSGNILUY.js +0 -1
- package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +0 -21
- package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +0 -1
- package/dist/m/plugins/wire-ZWVWKF76.js +0 -1
- package/dist/m/reader-core-ILWLXVMP.js +0 -68
- package/dist/m/route/route-KOCZSEGN.js +0 -1
- package/dist/m/sync/live-sync-4AUVXVJU.js +0 -1
- package/dist/m/ui/index-EVHKMKBL.js +0 -1
- package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
- /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
- /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-RC3L2F6X.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
package/src/plugins/sortable.js
CHANGED
|
@@ -250,6 +250,7 @@ function makeSortable(sortableElem, Sortable, record = loose) {
|
|
|
250
250
|
let press = null;
|
|
251
251
|
let restoring = null;
|
|
252
252
|
let started = false;
|
|
253
|
+
let native = false;
|
|
253
254
|
let ends = [];
|
|
254
255
|
// A press that ends without a drop (a click, a cancel) still has to hand the
|
|
255
256
|
// gesture's exclusion back: the drop itself releases it once its entry is recorded.
|
|
@@ -271,7 +272,11 @@ function makeSortable(sortableElem, Sortable, record = loose) {
|
|
|
271
272
|
ends = [];
|
|
272
273
|
if (pending) later(record, () => restoreRuntimeAttrs(pending));
|
|
273
274
|
};
|
|
274
|
-
const settleSoon = () => {
|
|
275
|
+
const settleSoon = (event) => {
|
|
276
|
+
// Chrome cancels the pointer as a native drag begins, just before Sortable's onStart:
|
|
277
|
+
// that press ends on the drop or dragend, not on the cancel.
|
|
278
|
+
if (native && event?.type === 'pointercancel') return;
|
|
279
|
+
native = false;
|
|
275
280
|
// Only the press that became a drag holds: a click has nothing to keep out of
|
|
276
281
|
// history, and its own handler's writes are the next entry.
|
|
277
282
|
if (started) {
|
|
@@ -291,9 +296,11 @@ function makeSortable(sortableElem, Sortable, record = loose) {
|
|
|
291
296
|
recorded = recordRuntimeAttrs(item);
|
|
292
297
|
press = beginMove(item);
|
|
293
298
|
started = false;
|
|
299
|
+
native = false;
|
|
294
300
|
record.presses.add(recorded);
|
|
295
301
|
pressed.add(recorded);
|
|
296
302
|
ends = ENDS.map(type => track(record, document, type, settleSoon, true));
|
|
303
|
+
ends.push(track(record, document, 'dragstart', () => { native = true; }, true));
|
|
297
304
|
}, true);
|
|
298
305
|
options.onUnchoose = restoreSoon;
|
|
299
306
|
// The one signal a click never makes: Sortable says the press became a drag, so this
|
package/src/plugins/source.js
CHANGED
|
@@ -191,6 +191,12 @@ export const source = {
|
|
|
191
191
|
unpaired: () => (session ? session.unpaired() : null),
|
|
192
192
|
/** The save pipeline's own ask: a promise to wait for before its capture, or null. */
|
|
193
193
|
ensureForSave,
|
|
194
|
+
/**
|
|
195
|
+
* How many times this session's map has been rebuilt, or 0 before it installs.
|
|
196
|
+
*/
|
|
197
|
+
get repairGeneration() {
|
|
198
|
+
return session ? session.repairGeneration() : 0;
|
|
199
|
+
},
|
|
194
200
|
};
|
|
195
201
|
|
|
196
202
|
/**
|
package/src/plugins/upgrade.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// a visitor, a draft, no network, a failed fetch, a bad digest or an equal version.
|
|
10
10
|
// The 24-hour cache is hyper-html-api's own; the dotted-version comparison is
|
|
11
11
|
// core/version.js, so the rules engine that upgrades a document stays the click's.
|
|
12
|
-
import { isNewerVersion } from "../core/version.js";
|
|
12
|
+
import { isNewerVersion, parseVersion } from "../core/version.js";
|
|
13
13
|
|
|
14
14
|
const CACHE_PREFIX = "hha:upgrade-check:";
|
|
15
15
|
const TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -77,12 +77,9 @@ async function latestManifest(sourceHref, currentVersion, { fetchFn, now }) {
|
|
|
77
77
|
return { latest, latestUrl };
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
fetchFn = (url, init) => fetch(url, init),
|
|
84
|
-
now = Date.now(),
|
|
85
|
-
} = {}) {
|
|
80
|
+
// The copy this page says it is: its version and the source it was copied from, or
|
|
81
|
+
// null for a page that is not a copy (or a person who cannot act on an offer).
|
|
82
|
+
function copyOf({ doc, loc }) {
|
|
86
83
|
const clay = (typeof window !== "undefined" && window.clay) || {};
|
|
87
84
|
// A visitor cannot act on the offer, and a draft is not the document the source
|
|
88
85
|
// would replace. Neither fetches.
|
|
@@ -101,32 +98,64 @@ export async function findUpdate({
|
|
|
101
98
|
if (!source.pathname.endsWith(".html")) return null;
|
|
102
99
|
// A pristine published copy points at itself: it is the source, not a copy of one.
|
|
103
100
|
if (loc && source.origin === loc.origin && source.pathname === loc.pathname) return null;
|
|
101
|
+
return { source, currentVersion };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// What the check learned. "current" is said only after the source answered and named
|
|
105
|
+
// nothing newer; anything that stopped the check short is "unknown", and so is an
|
|
106
|
+
// update the person already declined.
|
|
107
|
+
export async function checkUpdate({
|
|
108
|
+
doc = document,
|
|
109
|
+
loc = location,
|
|
110
|
+
fetchFn = (url, init) => fetch(url, init),
|
|
111
|
+
now = Date.now(),
|
|
112
|
+
} = {}) {
|
|
113
|
+
const copy = copyOf({ doc, loc });
|
|
114
|
+
if (!copy) return { status: "none", version: null, update: null };
|
|
115
|
+
const { source, currentVersion } = copy;
|
|
116
|
+
const result = (status, update = null) => ({ status, version: currentVersion, update });
|
|
104
117
|
const latestUrl = new URL(source.href);
|
|
105
118
|
latestUrl.pathname = latestUrl.pathname.replace(/\.html$/, ".json");
|
|
106
119
|
|
|
107
120
|
const { latest } = await latestManifest(source.href, currentVersion, { fetchFn, now });
|
|
108
|
-
if (!latest || typeof latest !== "object") return
|
|
109
|
-
if (
|
|
121
|
+
if (!latest || typeof latest !== "object" || typeof latest.version !== "string") return result("unknown");
|
|
122
|
+
if (!parseVersion(latest.version) || !parseVersion(currentVersion)) return result("unknown");
|
|
123
|
+
if (!isNewerVersion(latest.version, currentVersion)) return result("current");
|
|
110
124
|
|
|
125
|
+
const clay = (typeof window !== "undefined" && window.clay) || {};
|
|
111
126
|
const storage = clay.storage && clay.storage.local;
|
|
112
|
-
if (storage && storage.get(DISMISSED_KEY) === latest.version) return
|
|
127
|
+
if (storage && storage.get(DISMISSED_KEY) === latest.version) return result("unknown");
|
|
113
128
|
|
|
114
|
-
if (typeof latest.html !== "string" || !latest.html) return
|
|
129
|
+
if (typeof latest.html !== "string" || !latest.html) return result("unknown");
|
|
115
130
|
const htmlUrl = new URL(latest.html, latestUrl).href;
|
|
116
131
|
const res = await fetchOrNull(fetchFn, htmlUrl);
|
|
117
|
-
if (!res) return
|
|
132
|
+
if (!res) return result("unknown");
|
|
118
133
|
let bytes;
|
|
119
134
|
try {
|
|
120
135
|
bytes = await res.arrayBuffer();
|
|
121
136
|
} catch {
|
|
122
|
-
return
|
|
137
|
+
return result("unknown");
|
|
123
138
|
}
|
|
124
139
|
const digest = await digestOf(bytes);
|
|
125
140
|
// A manifest whose digest does not match its HTML is a host that served the wrong
|
|
126
141
|
// bytes. Nothing is offered, because nothing can be trusted to be that version.
|
|
127
|
-
if (!digest || digest !== String(latest.sha256 || "").toLowerCase()) return
|
|
142
|
+
if (!digest || digest !== String(latest.sha256 || "").toLowerCase()) return result("unknown");
|
|
128
143
|
|
|
129
|
-
return { version: latest.version, html: htmlUrl, sha256: digest, latestUrl: latestUrl.href };
|
|
144
|
+
return result("update", { version: latest.version, html: htmlUrl, sha256: digest, latestUrl: latestUrl.href });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function findUpdate(options) {
|
|
148
|
+
return (await checkUpdate(options)).update;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// The menu's footer while and after the check: the bare version until the check proves
|
|
152
|
+
// more, then the latest-version line or, when there is one, the upgrade itself.
|
|
153
|
+
async function showVersion(status, version) {
|
|
154
|
+
const clay = window.clay;
|
|
155
|
+
await whenMenuReady(clay);
|
|
156
|
+
if (!menuReady(clay) || !version) return;
|
|
157
|
+
const label = status === "current" ? `This doc is at latest version (${version})` : `Version ${version}`;
|
|
158
|
+
clay.menu.addItem({ id: "upgrade", label, text: true });
|
|
130
159
|
}
|
|
131
160
|
|
|
132
161
|
function dismiss(version) {
|
|
@@ -184,8 +213,13 @@ export async function start(options = {}) {
|
|
|
184
213
|
const clay = window.clay;
|
|
185
214
|
// Toasts and the menu are drawn by satellites that may land after boot settles.
|
|
186
215
|
await Promise.resolve(clay && clay.loaded && clay.loaded.ui).catch(() => {});
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
216
|
+
const copy = copyOf({ doc: options.doc || document, loc: options.loc || location });
|
|
217
|
+
if (copy) showVersion("checking", copy.currentVersion);
|
|
218
|
+
const { status, version, update } = await checkUpdate(options).catch(() => ({ status: "unknown", version: copy && copy.currentVersion, update: null }));
|
|
219
|
+
if (!update) {
|
|
220
|
+
if (status !== "none") await showVersion(status, version);
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
189
223
|
await announce(update);
|
|
190
224
|
return update;
|
|
191
225
|
}
|
package/src/plugins/upload-ui.js
CHANGED
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
* stylesheet load the first time somebody mounts one, so a document that never
|
|
14
14
|
* uploads never downloads them.
|
|
15
15
|
*
|
|
16
|
-
* The
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* The row draws inside its own shadow root, so the page's CSS cannot restyle it. Its
|
|
17
|
+
* look is a set of `--clay-upload-*` custom properties, each with a default in its own
|
|
18
|
+
* `var()`, and those do reach in: a container that sets one — `.journal-drop {
|
|
19
|
+
* --clay-upload-ink: #333 }` — restyles the row without replacing any of its
|
|
20
|
+
* behaviour. The font family is the one thing it still inherits from the page.
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
23
|
import { canRetry } from "./upload-task.js";
|
|
24
|
+
import { mountEditorUI } from "../ui/editor-ui.js";
|
|
22
25
|
|
|
23
26
|
const CSS = `.clay-upload {
|
|
24
27
|
display: flex;
|
|
@@ -110,16 +113,8 @@ const CSS = `.clay-upload {
|
|
|
110
113
|
.clay-upload__button { color: var(--clay-upload-button-ink, #8AB4FF); }
|
|
111
114
|
.clay-upload[data-clay-upload-state="failed"] .clay-upload__label { color: var(--clay-upload-failure-ink, #FFB4AB); }
|
|
112
115
|
}
|
|
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
|
-
}
|
|
116
|
+
@media print { .clay-upload { display: none; } }
|
|
117
|
+
:host { font-family: inherit !important; }`;
|
|
123
118
|
|
|
124
119
|
function percentOf({ loaded, total, percent }) {
|
|
125
120
|
if (typeof percent === "number") return percent;
|
|
@@ -143,8 +138,6 @@ export function mount(task, container) {
|
|
|
143
138
|
throw new TypeError("clay.upload.mount needs a container");
|
|
144
139
|
}
|
|
145
140
|
|
|
146
|
-
stylesOnce();
|
|
147
|
-
|
|
148
141
|
const name = task.file?.name || "File";
|
|
149
142
|
|
|
150
143
|
const root = document.createElement("div");
|
|
@@ -237,9 +230,11 @@ export function mount(task, container) {
|
|
|
237
230
|
if (!retryEl.hidden) retryEl.setAttribute("aria-label", lastError ? `Retry ${name}: ${lastError}` : `Retry ${name}`);
|
|
238
231
|
}
|
|
239
232
|
|
|
233
|
+
let ui = null;
|
|
234
|
+
|
|
240
235
|
function unmount() {
|
|
241
236
|
unsubscribe();
|
|
242
|
-
|
|
237
|
+
ui?.remove();
|
|
243
238
|
}
|
|
244
239
|
|
|
245
240
|
cancelEl.addEventListener("click", () => {
|
|
@@ -257,7 +252,7 @@ export function mount(task, container) {
|
|
|
257
252
|
});
|
|
258
253
|
|
|
259
254
|
const unsubscribe = task.subscribe(draw);
|
|
260
|
-
|
|
255
|
+
ui = mountEditorUI({ name: "upload", content: root, css: [CSS], parent: host });
|
|
261
256
|
|
|
262
257
|
return { unmount };
|
|
263
258
|
}
|
|
@@ -4,12 +4,20 @@ export function announceBottomUi() {
|
|
|
4
4
|
document.dispatchEvent(new CustomEvent(BOTTOM_UI_CHANGED));
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
// ClayJS's own interfaces sit in shadow roots, where a hit test stops at the host, and the
|
|
8
|
+
// host takes no box of its own. So a hit on one looks inside it too.
|
|
9
|
+
function hitsAt(scope, x, y) {
|
|
10
|
+
const hits = scope.elementsFromPoint ? scope.elementsFromPoint(x, y) : [];
|
|
11
|
+
return hits
|
|
12
|
+
.filter((hit) => hit.getRootNode() === scope)
|
|
13
|
+
.flatMap((hit) => (hit.localName === "clay-editor-ui" && hit.shadowRoot ? hitsAt(hit.shadowRoot, x, y) : [hit]));
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
export function bottomBarHeight(x, skip) {
|
|
8
17
|
const h = window.innerHeight;
|
|
9
18
|
let raise = 0;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
for (let el = hit; el && el !== document.body && el !== document.documentElement; el = el.parentElement) {
|
|
19
|
+
for (const hit of hitsAt(document, x, h - 3)) {
|
|
20
|
+
for (let el = hit; el && el !== document.body && el !== document.documentElement; el = el.parentElement ?? el.getRootNode().host) {
|
|
13
21
|
if (skip(el)) break;
|
|
14
22
|
const position = getComputedStyle(el).position;
|
|
15
23
|
if (position === "fixed" || position === "sticky") {
|
|
@@ -23,7 +31,7 @@ export function bottomBarHeight(x, skip) {
|
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
export function promptPanel(box) {
|
|
26
|
-
const panel = document.querySelector("#hcp:not([hidden])");
|
|
34
|
+
const panel = document.querySelector('clay-editor-ui[data-clay-ui="download-prompt"]')?.shadowRoot.querySelector("#hcp:not([hidden])");
|
|
27
35
|
if (!panel) return 0;
|
|
28
36
|
const r = panel.getBoundingClientRect();
|
|
29
37
|
if (!r.width || !r.height || r.right <= box.left || r.left >= box.right) return 0;
|
|
@@ -4,6 +4,12 @@ body:has(> .clay-intro) { height: auto !important; overflow: visible !important;
|
|
|
4
4
|
position: relative;
|
|
5
5
|
display: block;
|
|
6
6
|
}
|
|
7
|
+
@media print { .clay-intro { display: none; } }
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
// The close button sits in its own shadow root inside the author's strip, so only it is
|
|
11
|
+
// shielded from the page's CSS. It still takes the strip's text colour.
|
|
12
|
+
export const CLOSE_CSS = `:host { color: inherit !important; }
|
|
7
13
|
.clay-intro__close {
|
|
8
14
|
position: absolute; top: 10px; right: 10px; z-index: 1;
|
|
9
15
|
display: inline-flex; align-items: center; justify-content: center;
|
|
@@ -11,9 +17,8 @@ body:has(> .clay-intro) { height: auto !important; overflow: visible !important;
|
|
|
11
17
|
border: 0; border-radius: 4px; background: none; box-shadow: none;
|
|
12
18
|
color: inherit; opacity: .55; cursor: pointer; line-height: 1;
|
|
13
19
|
}
|
|
14
|
-
.clay-
|
|
20
|
+
.clay-intro__close:hover { opacity: 1; background: none; color: inherit; }
|
|
15
21
|
.clay-intro__close:focus-visible { opacity: 1; outline: 2px solid currentColor; outline-offset: 2px; }
|
|
16
22
|
.clay-intro__close svg { display: block; width: 12px; height: 12px; fill: currentColor; stroke: none; }
|
|
17
23
|
@media (pointer: coarse) { .clay-intro__close { width: 44px; height: 44px; top: 4px; right: 4px; } }
|
|
18
|
-
@media print { .clay-intro { display: none; } }
|
|
19
24
|
`;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { INTRO_CSS } from "./intro-style.js";
|
|
1
|
+
import { CLOSE_CSS, INTRO_CSS } from "./intro-style.js";
|
|
2
|
+
import { mountEditorUI } from "../../ui/editor-ui.js";
|
|
2
3
|
import { announceBottomUi } from "./bottom-ui.js";
|
|
4
|
+
import { bootPhase } from "../../core/boot-phase.js";
|
|
3
5
|
|
|
4
6
|
export const COPY = {
|
|
5
7
|
close: "Hide this introduction",
|
|
@@ -45,7 +47,7 @@ export function mountIntro({ memory, template, appName, hadDraft }) {
|
|
|
45
47
|
if (!template || !template.content.textContent.trim() && !template.content.querySelector("img, svg, video, canvas")) return null;
|
|
46
48
|
if (!forced()) {
|
|
47
49
|
if (memory.get().intro.done || hadDraft) return null;
|
|
48
|
-
if (
|
|
50
|
+
if (bootPhase.servedHash) return null;
|
|
49
51
|
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
50
52
|
}
|
|
51
53
|
return mount({ memory, template, appName });
|
|
@@ -66,7 +68,8 @@ function mount({ memory, template, appName }) {
|
|
|
66
68
|
close.className = "clay-intro__close";
|
|
67
69
|
close.setAttribute("aria-label", COPY.close);
|
|
68
70
|
close.innerHTML = '<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M1.4 0 6 4.6 10.6 0 12 1.4 7.4 6 12 10.6 10.6 12 6 7.4 1.4 12 0 10.6 4.6 6 0 1.4z"/></svg>';
|
|
69
|
-
|
|
71
|
+
close.addEventListener("click", () => dismiss("closed"));
|
|
72
|
+
mountEditorUI({ name: "intro-close", content: close, css: [CLOSE_CSS], parent: section });
|
|
70
73
|
|
|
71
74
|
let gone = false;
|
|
72
75
|
let frame = 0;
|
|
@@ -100,7 +103,7 @@ function mount({ memory, template, appName }) {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
section.addEventListener("click", (event) => {
|
|
103
|
-
if (event.target.closest("
|
|
106
|
+
if (event.target.closest("[visitor-intro-dismiss]")) {
|
|
104
107
|
dismiss("closed");
|
|
105
108
|
return;
|
|
106
109
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isDraftMode } from "../../core/is-edit-mode.js";
|
|
2
2
|
import { BOTTOM_UI_CHANGED, announceBottomUi, bottomBarHeight, promptPanel } from "./bottom-ui.js";
|
|
3
|
+
import { mountEditorUI } from "../../ui/editor-ui.js";
|
|
3
4
|
|
|
4
5
|
export const COPY = {
|
|
5
6
|
draft: "You're seeing a private draft, like a visitor.",
|
|
@@ -77,18 +78,10 @@ const CHIP_CSS = `.clay-owner {
|
|
|
77
78
|
@media print { .clay-owner { display: none; } }
|
|
78
79
|
`;
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
if (document.getElementById("clay-owner-style")) return;
|
|
82
|
-
const style = document.createElement("style");
|
|
83
|
-
style.id = "clay-owner-style";
|
|
84
|
-
style.setAttribute("clay", "editor-ui");
|
|
85
|
-
style.textContent = CHIP_CSS;
|
|
86
|
-
document.head.appendChild(style);
|
|
87
|
-
}
|
|
81
|
+
export const ownerChip = () => document.querySelector('clay-editor-ui[data-clay-ui="owner"]')?.shadowRoot.getElementById("clay-owner") ?? null;
|
|
88
82
|
|
|
89
83
|
export function mountOwnerChip() {
|
|
90
|
-
if (
|
|
91
|
-
stylesOnce();
|
|
84
|
+
if (ownerChip()) return null;
|
|
92
85
|
|
|
93
86
|
const draft = isDraftMode;
|
|
94
87
|
const chip = document.createElement("div");
|
|
@@ -118,10 +111,10 @@ export function mountOwnerChip() {
|
|
|
118
111
|
close.className = "clay-owner__x";
|
|
119
112
|
close.setAttribute("aria-label", COPY.close);
|
|
120
113
|
close.textContent = "\u00d7";
|
|
121
|
-
close.addEventListener("click", () => {
|
|
114
|
+
close.addEventListener("click", () => { ui.remove(); liftOff(); release(); });
|
|
122
115
|
|
|
123
116
|
chip.append(lead, action, close);
|
|
124
|
-
|
|
117
|
+
const ui = mountEditorUI({ name: "owner", content: chip, css: [CHIP_CSS], attributes: { "sap-ignore": "" } });
|
|
125
118
|
|
|
126
119
|
const vars = document.createElement("style");
|
|
127
120
|
vars.id = "clay-owner-vars";
|
|
@@ -172,10 +165,13 @@ export function mountOwnerChip() {
|
|
|
172
165
|
document.addEventListener("transitionend", lift, true);
|
|
173
166
|
document.addEventListener("pointerup", lift, true);
|
|
174
167
|
window.addEventListener("hashchange", lift);
|
|
168
|
+
// Transition events stop at a shadow root, so the chip's own changes are heard here.
|
|
169
|
+
chip.addEventListener("transitionend", lift);
|
|
175
170
|
lift();
|
|
176
171
|
// Whoever removes the chip takes its listeners back with it: a chip mounted and unmounted
|
|
177
172
|
// once per mode session would otherwise leave six behind each time.
|
|
178
173
|
chip.liftOff = liftOff;
|
|
174
|
+
chip.unmount = () => { liftOff(); ui.remove(); };
|
|
179
175
|
|
|
180
176
|
return chip;
|
|
181
177
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { announceBottomUi, bottomBarHeight } from "./bottom-ui.js";
|
|
2
|
-
|
|
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";
|
|
4
|
-
|
|
2
|
+
import { mountEditorUI } from "../../ui/editor-ui.js";
|
|
5
3
|
const ASIDE = `<aside class="hcp" id="hcp" hidden aria-label="Download this app" data-hcp-state="tab" data-hcp-reason="open" sap-ignore clay="editor-ui">
|
|
6
4
|
<section class="hcp-card" id="hcp-card" role="dialog" aria-modal="false" aria-labelledby="hcp-title">
|
|
7
5
|
<button type="button" class="hcp-x" data-hcp="minimize" aria-label="Minimize"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" aria-hidden="true"><path d="M5 12h14"/></svg></button>
|
|
@@ -24,13 +22,15 @@ const ASIDE = `<aside class="hcp" id="hcp" hidden aria-label="Download this app"
|
|
|
24
22
|
</aside>`;
|
|
25
23
|
|
|
26
24
|
export async function mountPrompt({ memory, appName, link }) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
const { PROMPT_CSS } = await import("./prompt-style.js");
|
|
26
|
+
const existing = document.querySelector('clay-editor-ui[data-clay-ui="download-prompt"]');
|
|
27
|
+
let root = existing?.shadowRoot.getElementById("hcp");
|
|
28
|
+
if (!root) {
|
|
29
|
+
const parsed = document.createElement("template");
|
|
30
|
+
parsed.innerHTML = ASIDE;
|
|
31
|
+
root = parsed.content.firstElementChild;
|
|
32
|
+
mountEditorUI({ name: "download-prompt", content: root, css: [PROMPT_CSS], attributes: { "sap-ignore": "" } });
|
|
30
33
|
}
|
|
31
|
-
if (!document.getElementById("hcp")) document.body.insertAdjacentHTML("beforeend", ASIDE);
|
|
32
|
-
|
|
33
|
-
const root = document.getElementById("hcp");
|
|
34
34
|
const ui = memory.get();
|
|
35
35
|
if (!ui.prompt || typeof ui.prompt !== "object") ui.prompt = {};
|
|
36
36
|
const mem = ui.prompt;
|
|
@@ -158,6 +158,8 @@ export async function mountPrompt({ memory, appName, link }) {
|
|
|
158
158
|
addEventListener("scroll", lift, { passive: true });
|
|
159
159
|
addEventListener("resize", lift);
|
|
160
160
|
document.addEventListener("transitionend", lift, true);
|
|
161
|
+
// Transition events stop at a shadow root, so the prompt's own slides are heard here.
|
|
162
|
+
root.addEventListener("transitionend", lift);
|
|
161
163
|
|
|
162
164
|
set(mem.shown || mem.dismissed ? "mini" : "tab");
|
|
163
165
|
if (!mem.shown && (mem.uses || 0) >= USES_TO_SHOW) maybeShow("return");
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { draftPath } from "./store.js";
|
|
2
|
+
|
|
1
3
|
const SRC = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
2
4
|
|
|
3
5
|
export function rememberSource(memory, search = window.location.search) {
|
|
@@ -9,8 +11,7 @@ export function rememberSource(memory, search = window.location.search) {
|
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export function downloadUrl(src, loc = window.location) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return `/_/download${path}?as=htmlclay${src ? `&src=${encodeURIComponent(src)}` : ""}`;
|
|
14
|
+
const path = draftPath(loc.pathname);
|
|
15
|
+
return `/_/download${path === "/" ? "" : path}?as=htmlclay${src ? `&src=${encodeURIComponent(src)}` : ""}`;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -4,6 +4,20 @@ import { draftSlot, setDraftStoreFailed } from "../../core/draft-mode.js";
|
|
|
4
4
|
export const KEY_PREFIX = "clay:visitor-drafts:";
|
|
5
5
|
export const RECORD_VERSION = 1;
|
|
6
6
|
|
|
7
|
+
// The document a pathname names, spelled the way the hosts resolve it: the path up to
|
|
8
|
+
// and including the first segment that ends in .html or .htmlclay (anything after it is
|
|
9
|
+
// a route inside that document), repeated slashes as one, the root index.html as "/",
|
|
10
|
+
// and "/" for a host that serves one document at every path.
|
|
11
|
+
export function draftPath(pathname = window.location.pathname) {
|
|
12
|
+
const clean = String(pathname || "/").replace(/\/{2,}/g, "/");
|
|
13
|
+
const file = clean.match(/^(.*?\/[^/]+\.(?:html|htmlclay))(?:\/|$)/i);
|
|
14
|
+
const path = file ? file[1] : "/";
|
|
15
|
+
return path.toLowerCase() === "/index.html" ? "/" : path;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const legacyKey = (slot) => KEY_PREFIX + slot;
|
|
19
|
+
export const draftKey = (slot, path = draftPath()) => `${KEY_PREFIX}${slot}@${path}`;
|
|
20
|
+
|
|
7
21
|
export function safeStorage() {
|
|
8
22
|
try { return window.localStorage; } catch { return null; }
|
|
9
23
|
}
|
|
@@ -66,26 +80,88 @@ function complete(draft, page, top = false) {
|
|
|
66
80
|
return draft;
|
|
67
81
|
}
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
83
|
+
const sameIds = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
|
|
84
|
+
|
|
85
|
+
// Rows matched by id, when neither tab reordered the rows all three still have: a row
|
|
86
|
+
// one tab added is kept where that tab put it, a row one tab removed goes unless the
|
|
87
|
+
// other tab changed it, and a row both kept merges field by field. Anything else is
|
|
88
|
+
// null, and the caller treats the list as one value.
|
|
89
|
+
function mergeRows(base, mine, theirs) {
|
|
90
|
+
const index = (rows) => new Map(rows.map((row) => [row.id, row]));
|
|
91
|
+
const was = index(base);
|
|
92
|
+
const mineById = index(mine);
|
|
93
|
+
const theirsById = index(theirs);
|
|
94
|
+
const shared = (rows) => rows.filter((row) => was.has(row.id) && mineById.has(row.id) && theirsById.has(row.id)).map((row) => row.id);
|
|
95
|
+
const order = shared(base);
|
|
96
|
+
if (!sameIds(order, shared(mine)) || !sameIds(order, shared(theirs))) return null;
|
|
97
|
+
for (const [id, row] of was) {
|
|
98
|
+
if (mineById.has(id) && !theirsById.has(id) && !same(mineById.get(id), row)) return null;
|
|
99
|
+
if (!mineById.has(id) && theirsById.has(id) && !same(theirsById.get(id), row)) return null;
|
|
100
|
+
}
|
|
101
|
+
let conflict = false;
|
|
102
|
+
const out = [];
|
|
103
|
+
for (const row of mine) {
|
|
104
|
+
if (was.has(row.id) && !theirsById.has(row.id)) continue;
|
|
105
|
+
if (!theirsById.has(row.id)) { out.push(row); continue; }
|
|
106
|
+
const part = mergeValue(was.get(row.id), row, theirsById.get(row.id), true);
|
|
107
|
+
conflict ||= part.conflict;
|
|
108
|
+
out.push(part.value);
|
|
109
|
+
}
|
|
110
|
+
theirs.forEach((row, i) => {
|
|
111
|
+
if (was.has(row.id) || mineById.has(row.id)) return;
|
|
112
|
+
let at = 0;
|
|
113
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
114
|
+
const k = out.findIndex((kept) => kept.id === theirs[j].id);
|
|
115
|
+
if (k !== -1) { at = k + 1; break; }
|
|
116
|
+
}
|
|
117
|
+
out.splice(at, 0, row);
|
|
118
|
+
});
|
|
119
|
+
return { value: out, conflict };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mergeValue(base, mine, theirs, rows) {
|
|
74
123
|
if (same(mine, theirs) || same(theirs, base)) return { value: mine, conflict: false };
|
|
75
124
|
if (same(mine, base)) return { value: theirs, conflict: false };
|
|
76
125
|
if (isPlain(mine) && isPlain(theirs)) {
|
|
77
126
|
const value = {};
|
|
78
127
|
let conflict = false;
|
|
79
128
|
for (const name of new Set([...Object.keys(mine), ...Object.keys(theirs)])) {
|
|
80
|
-
const part =
|
|
129
|
+
const part = mergeValue(isPlain(base) ? base[name] : undefined, mine[name], theirs[name], rows);
|
|
81
130
|
conflict ||= part.conflict;
|
|
82
131
|
if (part.value !== undefined) value[name] = part.value;
|
|
83
132
|
}
|
|
84
133
|
return { value, conflict };
|
|
85
134
|
}
|
|
135
|
+
if (rows && hasIds(base) && hasIds(mine) && hasIds(theirs)) {
|
|
136
|
+
const merged = mergeRows(base, mine, theirs);
|
|
137
|
+
if (merged) return merged;
|
|
138
|
+
}
|
|
86
139
|
return { value: mine, conflict: true };
|
|
87
140
|
}
|
|
88
141
|
|
|
142
|
+
function countIds(value, counts = new Map()) {
|
|
143
|
+
if (Array.isArray(value)) {
|
|
144
|
+
if (hasIds(value)) for (const row of value) counts.set(row.id, (counts.get(row.id) || 0) + 1);
|
|
145
|
+
for (const item of value) countIds(item, counts);
|
|
146
|
+
} else if (isPlain(value)) {
|
|
147
|
+
for (const item of Object.values(value)) countIds(item, counts);
|
|
148
|
+
}
|
|
149
|
+
return counts;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Three-way merge of this tab's value with another tab's, from the value both started
|
|
153
|
+
// at. Objects merge per field and lists of rows with ids merge per row. A list merge that
|
|
154
|
+
// would hold one id in two places (a card moved to different lists in each tab) is
|
|
155
|
+
// undone, and that list is a conflict this tab wins, as is any reorder.
|
|
156
|
+
export function merge3(base, mine, theirs) {
|
|
157
|
+
const merged = mergeValue(base, mine, theirs, true);
|
|
158
|
+
const before = countIds(mine);
|
|
159
|
+
for (const [id, count] of countIds(merged.value)) {
|
|
160
|
+
if (count > 1 && count > (before.get(id) || 0)) return mergeValue(base, mine, theirs, false);
|
|
161
|
+
}
|
|
162
|
+
return merged;
|
|
163
|
+
}
|
|
164
|
+
|
|
89
165
|
function storageFailure(doc, error) {
|
|
90
166
|
setDraftStoreFailed(true);
|
|
91
167
|
doc.dispatchEvent(new CustomEvent("clay:drafts-store-failed", { detail: { error } }));
|
|
@@ -94,9 +170,58 @@ function storageFailure(doc, error) {
|
|
|
94
170
|
return failure;
|
|
95
171
|
}
|
|
96
172
|
|
|
97
|
-
|
|
173
|
+
const savedAtOf = (raw) => {
|
|
174
|
+
try { return String(JSON.parse(raw)?.savedAt ?? ""); } catch { return ""; }
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// A draft saved before drafts were scoped to the document's path lives under the slot
|
|
178
|
+
// alone. It is copied into this path's key with its UI memory, and never written or
|
|
179
|
+
// removed: a tab still on an older ClayJS may write it again, and another document on
|
|
180
|
+
// this origin with the same slot may still need it. The marker keeps the old record's
|
|
181
|
+
// savedAt, so a later write by such a tab is caught up on the next load, and a reset
|
|
182
|
+
// never brings the old draft back.
|
|
183
|
+
export function adoptLegacy(slot, { path = draftPath(), storage = safeStorage() } = {}) {
|
|
184
|
+
const from = legacyKey(slot);
|
|
185
|
+
const to = draftKey(slot, path);
|
|
186
|
+
const marker = to + ":legacy";
|
|
187
|
+
try {
|
|
188
|
+
const generation = storage?.getItem(to);
|
|
189
|
+
const old = storage?.getItem(from);
|
|
190
|
+
if (old == null) {
|
|
191
|
+
// A visitor who only dismissed the intro has UI memory and no draft; it is copied once too.
|
|
192
|
+
const ui = storage?.getItem(from + ":ui");
|
|
193
|
+
if (ui != null && storage.getItem(to + ":ui") == null && storage.getItem(to + ":ui-legacy") == null) {
|
|
194
|
+
storage.setItem(to + ":ui", ui);
|
|
195
|
+
storage.setItem(to + ":ui-legacy", "1");
|
|
196
|
+
}
|
|
197
|
+
return "none";
|
|
198
|
+
}
|
|
199
|
+
const stamp = savedAtOf(old);
|
|
200
|
+
const seen = storage.getItem(marker);
|
|
201
|
+
if (seen === stamp) return "none";
|
|
202
|
+
// A visitor who wrote to this document while the copy ran keeps what they typed.
|
|
203
|
+
const current = storage.getItem(to);
|
|
204
|
+
if (current !== generation) return "none";
|
|
205
|
+
if (current == null || (seen != null && savedAtOf(current) === seen)) {
|
|
206
|
+
storage.setItem(to, old);
|
|
207
|
+
} else if (current !== old) {
|
|
208
|
+
storage.setItem(to + ":conflict", old);
|
|
209
|
+
console.warn("visitor-drafts: an older tab changed this draft after it moved; its copy was kept under", to + ":conflict");
|
|
210
|
+
}
|
|
211
|
+
if (seen == null) {
|
|
212
|
+
const ui = storage.getItem(from + ":ui");
|
|
213
|
+
if (ui != null && storage.getItem(to + ":ui") == null) storage.setItem(to + ":ui", ui);
|
|
214
|
+
}
|
|
215
|
+
storage.setItem(marker, stamp);
|
|
216
|
+
return "copied";
|
|
217
|
+
} catch {
|
|
218
|
+
return "failed";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function createStore({ doc = document, storage = safeStorage(), now = () => new Date(), path = draftPath(), fallback = null } = {}) {
|
|
98
223
|
const slot = draftSlot(doc);
|
|
99
|
-
const key =
|
|
224
|
+
const key = draftKey(slot, path);
|
|
100
225
|
const rules = engine.findRulesIn(doc, "api").rules;
|
|
101
226
|
|
|
102
227
|
let lastRaw = null;
|
|
@@ -105,7 +230,11 @@ export function createStore({ doc = document, storage = safeStorage(), now = ()
|
|
|
105
230
|
let stopped = false;
|
|
106
231
|
|
|
107
232
|
function getRaw() {
|
|
108
|
-
try {
|
|
233
|
+
try {
|
|
234
|
+
if (!storage) return null;
|
|
235
|
+
const raw = storage.getItem(key);
|
|
236
|
+
return raw == null && fallback ? storage.getItem(fallback) : raw;
|
|
237
|
+
} catch { return null; }
|
|
109
238
|
}
|
|
110
239
|
|
|
111
240
|
function parse(raw) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export function createUiMemory(
|
|
2
|
-
const key =
|
|
1
|
+
export function createUiMemory(draftKey, storage) {
|
|
2
|
+
const key = `${draftKey}:ui`;
|
|
3
3
|
const blank = () => ({ v: 1, prompt: {}, intro: { done: null }, src: null });
|
|
4
4
|
let mem = blank();
|
|
5
5
|
try {
|