@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/live.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
import { contains, parseBy, frameSet, descendants, unionRanges, formatBy } from "../live/provenance.js";
|
|
9
9
|
import { createStore } from "../live/store.js";
|
|
10
10
|
import { serializeLive, stampOf, parseStamp, textHash } from "../live/ops.js";
|
|
11
|
-
import { onSnapshot } from "../core/snapshot.js";
|
|
12
|
-
import { onSaveAccepted, onSaveAttempt, onSaveOutcome, isSaveInProgress } from "../core/save-core.js";
|
|
11
|
+
import { capturePageState, onSnapshot } from "../core/snapshot.js";
|
|
12
|
+
import { onSaveAccepted, onSaveAttempt, onSaveOutcome, isSaveInProgress, saveFateIsUnknown, exitSave } from "../core/save-core.js";
|
|
13
13
|
import { savePageForce, baselineSettled, isSaveConflicted, autosaveIsSuspended } from "../core/save.js";
|
|
14
14
|
import { withKeepalive } from "../core/save-core.js";
|
|
15
15
|
import { isEditMode } from "../core/is-edit-mode.js";
|
|
@@ -17,13 +17,13 @@ import { registerUnsavedState } from "../lib/unsaved-state.js";
|
|
|
17
17
|
import { createCheckpoints } from "../live/checkpoint.js";
|
|
18
18
|
import { parseJsonRelaxed } from "../vendor/hyper-morph-json.vendor.js";
|
|
19
19
|
import * as hostMeta from "../core/host-meta.js";
|
|
20
|
-
import { createFastLane } from "../live/fast-lane.js";
|
|
21
20
|
import { createSaveHistoryRecovery, RECOVERY_WINDOW_MS } from "../live/save-history.js";
|
|
22
21
|
import { serverTransport } from "../live/server-lane.js";
|
|
23
|
-
import { localTransport, localLaneSupported } from "../live/local-lane.js";
|
|
24
22
|
import { captureYValue } from "../page/ybind/parts.js";
|
|
25
23
|
import { lastSeenEtag, recordEtag } from "../core/etag.js";
|
|
26
24
|
|
|
25
|
+
const lanes = () => Promise.all([import("../live/fast-lane.js"), import("../live/local-lane.js")]).then(([f, l]) => ({ createFastLane: f.createFastLane, localTransport: l.localTransport, localLaneSupported: l.localLaneSupported }));
|
|
26
|
+
|
|
27
27
|
const NAME = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
28
28
|
const entries = new Map();
|
|
29
29
|
|
|
@@ -755,6 +755,9 @@ function seedLocalRoom() {
|
|
|
755
755
|
if (value !== null && typeof value === "object") values.set(name, { value, ephemeral: tag.ephemeral });
|
|
756
756
|
} catch {}
|
|
757
757
|
}
|
|
758
|
+
for (const [name, entry] of entries) {
|
|
759
|
+
if (!entry.staged) values.set(name, { value: entry.store.authoritative, ephemeral: entry.store.ephemeral });
|
|
760
|
+
}
|
|
758
761
|
return values;
|
|
759
762
|
}
|
|
760
763
|
|
|
@@ -769,13 +772,22 @@ function ensureLane() {
|
|
|
769
772
|
if (request !== laneRequest) return;
|
|
770
773
|
serverLane = supported;
|
|
771
774
|
provenanceHost = provenance;
|
|
775
|
+
const lane = await lanes().catch(() => null);
|
|
776
|
+
if (request !== laneRequest) return;
|
|
777
|
+
if (!lane) {
|
|
778
|
+
settleLane?.(null);
|
|
779
|
+
settleWriter?.(null);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
772
782
|
if (supported) {
|
|
773
|
-
fast = createFastLane(laneOptions(serverTransport()));
|
|
774
|
-
} else if (localLaneSupported()) {
|
|
783
|
+
fast = lane.createFastLane(laneOptions(serverTransport()));
|
|
784
|
+
} else if (lane.localLaneSupported()) {
|
|
775
785
|
// The document's own floor picks the channel scope: a floor-1 document
|
|
776
|
-
// never shares the lane a tab that cannot read it is on.
|
|
777
|
-
|
|
778
|
-
|
|
786
|
+
// never shares the lane a tab that cannot read it is on. The room scope
|
|
787
|
+
// puts this tab's sequencer in the host-mode room, which a tab of the lane
|
|
788
|
+
// before it never joins.
|
|
789
|
+
local = lane.localTransport({ seed: seedLocalRoom, rooms: true, floor: document.querySelector('meta[name="clay-page-format"]')?.getAttribute("content") });
|
|
790
|
+
fast = lane.createFastLane(laneOptions(local));
|
|
779
791
|
} else {
|
|
780
792
|
settleLane?.(null);
|
|
781
793
|
settleWriter?.(null);
|
|
@@ -878,7 +890,9 @@ function receive(entry, text, source, dropIds) {
|
|
|
878
890
|
} catch {
|
|
879
891
|
return;
|
|
880
892
|
}
|
|
881
|
-
if (entry.store.rebase(value, { source, ...(dropIds?.length && { dropIds }) }))
|
|
893
|
+
if (!entry.store.rebase(value, { source, ...(dropIds?.length && { dropIds }) })) return false;
|
|
894
|
+
entry.framesSince = true;
|
|
895
|
+
return true;
|
|
882
896
|
}
|
|
883
897
|
|
|
884
898
|
// One open store and what the plugin follows about it. A staged store has no lane.
|
|
@@ -934,11 +948,12 @@ export function live(name) {
|
|
|
934
948
|
|
|
935
949
|
// A page whose file carries no state of its own yet: the seed in memory, no tag in
|
|
936
950
|
// the document, no request, until its first genuine op is written by the ordinary
|
|
937
|
-
// save.
|
|
938
|
-
export function stageStore(name, { value, represented = null, signal = null } = {}) {
|
|
951
|
+
// save. A value already stored by this tab or a peer can open its room.
|
|
952
|
+
export function stageStore(name, { value, represented = null, signal = null, persisted = false } = {}) {
|
|
939
953
|
const existing = entries.get(name);
|
|
940
954
|
if (existing && !existing.staged) throw new Error(`clay.live("${name}"): the store is open in a room`);
|
|
941
955
|
const entry = existing ?? openEntry(name, createStore({ name, value, ephemeral: false }), { staged: { represented, signal } });
|
|
956
|
+
if (persisted) activateStaged(entry);
|
|
942
957
|
return {
|
|
943
958
|
handle: entry.store.handle,
|
|
944
959
|
isPending: () => !!entry.staged && entry.store.pending.length > 0,
|
|
@@ -1235,11 +1250,47 @@ onSaveOutcome(({ captureId, result, attempts, sent }) => {
|
|
|
1235
1250
|
if (plan) startHistoryRecovery(plan, { result, attempts, startedAt: Date.now() });
|
|
1236
1251
|
});
|
|
1237
1252
|
|
|
1253
|
+
// The recovery integration's turn at an accepted save, when one journals this page.
|
|
1254
|
+
// Registered by page code once it holds a `page.yjs` channel, so a page that journals
|
|
1255
|
+
// nothing loads neither the proof builder nor the conditional client, and pays one null
|
|
1256
|
+
// check per save.
|
|
1257
|
+
let pageCheckpoints = null;
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* Hand every accepted save to a page checkpoint integration, or null to stop.
|
|
1261
|
+
*
|
|
1262
|
+
* The integration builds the proof from the capture's pinned page state and the bytes
|
|
1263
|
+
* the host took; nothing here decides what is covered. Its answer is not awaited: a save
|
|
1264
|
+
* that succeeded is saved whether or not a journal can be retired, and a failure there
|
|
1265
|
+
* must never reach the save's own outcome.
|
|
1266
|
+
*
|
|
1267
|
+
* @param {?Function} install - ({capture, state, article, etag}) => unknown
|
|
1268
|
+
*/
|
|
1269
|
+
export function registerPageCheckpoints(install) {
|
|
1270
|
+
pageCheckpoints = install;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function offerCheckpoint(captureId, html) {
|
|
1274
|
+
if (!pageCheckpoints) return;
|
|
1275
|
+
// Read synchronously, before this capture is released: the value is the one its page
|
|
1276
|
+
// tag carried, and the edits that followed it are not in these bytes.
|
|
1277
|
+
const state = capturePageState(captureId);
|
|
1278
|
+
if (!state) return;
|
|
1279
|
+
let article = null;
|
|
1280
|
+
try { article = parseHtml(html).querySelector("article[data-page]")?.innerHTML ?? null; }
|
|
1281
|
+
catch (_) { article = null; }
|
|
1282
|
+
const etag = lastSeenEtag();
|
|
1283
|
+
Promise.resolve()
|
|
1284
|
+
.then(() => pageCheckpoints({ capture: String(captureId), state, article, etag }))
|
|
1285
|
+
.catch(() => {});
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1238
1288
|
// An accepted body confirms the ops of the capture that sent it, and only those.
|
|
1239
1289
|
// A null context (a raw save) cannot consume a captured pin.
|
|
1240
1290
|
function handleAccepted(html, context) {
|
|
1241
1291
|
const captureId = context?.captureId;
|
|
1242
1292
|
if (captureId === null || captureId === undefined) return;
|
|
1293
|
+
offerCheckpoint(captureId, html);
|
|
1243
1294
|
const pin = capturePins.get(captureId);
|
|
1244
1295
|
if (!pin) return;
|
|
1245
1296
|
dropCapture(captureId);
|
|
@@ -1323,9 +1374,7 @@ export function receiveFrame(html, seq = null) {
|
|
|
1323
1374
|
else if (pageTag.getAttribute("live-by") !== by) pageTag.setAttribute("live-by", by);
|
|
1324
1375
|
}
|
|
1325
1376
|
if (entry && !entry.store.ephemeral) {
|
|
1326
|
-
|
|
1327
|
-
if (entry.staged) continue;
|
|
1328
|
-
if (entry.store.handle.status.mode === "file") receive(entry, text, "remote", dropIds);
|
|
1377
|
+
if (entry.store.handle.status.mode === "file" && receive(entry, text, "remote", dropIds)) activateStaged(entry);
|
|
1329
1378
|
continue;
|
|
1330
1379
|
}
|
|
1331
1380
|
if (pageTag && pageTag.textContent !== text) pageTag.textContent = text;
|
|
@@ -1378,12 +1427,74 @@ export function fastLane() {
|
|
|
1378
1427
|
return fast;
|
|
1379
1428
|
}
|
|
1380
1429
|
|
|
1430
|
+
// The local lane's transport, for the format gate: a floor-1 document shares a
|
|
1431
|
+
// channel scope no tab below that floor is on, and moving this tab there is the
|
|
1432
|
+
// gate's call (page/format-gate.js). Null until this tab has such a lane, and a
|
|
1433
|
+
// gate asks again on its next retry.
|
|
1434
|
+
// Whether any live store of this tab holds ops the room has not confirmed. A tab with
|
|
1435
|
+
// none has nothing a scope switch could strand, so it may move without a receipt.
|
|
1436
|
+
export function lanePending() {
|
|
1437
|
+
return sessionStores().some((entry) => entry.store.pending.length > 0);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
export function localLane() {
|
|
1441
|
+
return local;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1381
1444
|
// The page editor's pause save: checkpoint within ms of now. Only the room's
|
|
1382
1445
|
// writer saves, so in other tabs this changes nothing.
|
|
1383
1446
|
export function checkpointWithin(ms) {
|
|
1384
1447
|
checkpoints.within(ms);
|
|
1385
1448
|
}
|
|
1386
1449
|
|
|
1450
|
+
// The exit's own checkpoint: this tab's page input, saved once, under the permit the
|
|
1451
|
+
// exit minted and inside its deadline. It is not the background checkpoint: no timer is
|
|
1452
|
+
// armed and writer election is untouched, because a leave is an explicit save of this
|
|
1453
|
+
// tab's work, like the force save behind Cmd+S.
|
|
1454
|
+
//
|
|
1455
|
+
// One attempt per exit. A caller whose page input is still unconfirmed gets the answer
|
|
1456
|
+
// that names it, never a second write: an expired exit must not send anything later.
|
|
1457
|
+
export async function requestExitCheckpoint({ name, needsSave, permit, deadline, signal = null, keepalive = false }) {
|
|
1458
|
+
const over = () => signal?.aborted || Date.now() >= deadline;
|
|
1459
|
+
// Abortable, and it clears its own timer, so nothing is left waiting after the exit.
|
|
1460
|
+
const pause = () => new Promise((resolve) => {
|
|
1461
|
+
const done = () => {
|
|
1462
|
+
clearTimeout(timer);
|
|
1463
|
+
signal?.removeEventListener("abort", done);
|
|
1464
|
+
resolve();
|
|
1465
|
+
};
|
|
1466
|
+
const timer = setTimeout(done, 5);
|
|
1467
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
1468
|
+
});
|
|
1469
|
+
const failed = (code) => ({ ok: false, code });
|
|
1470
|
+
for (;;) {
|
|
1471
|
+
if (over()) return failed("timeout");
|
|
1472
|
+
if (!needsSave()) return { ok: true };
|
|
1473
|
+
if (isSaveConflicted()) return failed("conflict");
|
|
1474
|
+
if (saveFateIsUnknown() || historyPending()) return failed("save-unknown");
|
|
1475
|
+
const entry = entries.get(name);
|
|
1476
|
+
if (!entry) return failed("disposed");
|
|
1477
|
+
if (!baselineSettled() || isSaveInProgress()) {
|
|
1478
|
+
await pause();
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
// A fast capture holds the room's value, so this tab's own ops have to reach the
|
|
1482
|
+
// room before the capture is taken.
|
|
1483
|
+
if (entry.store.handle.status.mode === "fast" && entry.store.pending.length) {
|
|
1484
|
+
fast?.release(name);
|
|
1485
|
+
await pause();
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
if (over()) return failed("timeout");
|
|
1489
|
+
const save = exitSave();
|
|
1490
|
+
if (!save) return failed("checkpoint-failed");
|
|
1491
|
+
const result = await withKeepalive(keepalive, () => save(permit));
|
|
1492
|
+
if (over()) return failed("timeout");
|
|
1493
|
+
if (!result?.ok) return failed(result?.code ?? "checkpoint-failed");
|
|
1494
|
+
return needsSave() ? failed("page-not-checkpointed") : { ok: true };
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1387
1498
|
// Resolves { ok: true } once the file holds every live change this tab knows
|
|
1388
1499
|
// of now, or { ok: false, code: "timeout" }. The writer saves as soon as it
|
|
1389
1500
|
// can; another tab waits for the writer's save to land.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// The menu's editing items: Undo and Redo, Ask AI, and Download a copy. Each one calls
|
|
2
|
+
// the service that already does the job and keeps no second copy of its state.
|
|
3
|
+
import { modeRefusalText } from "../../core/mode-refusal.js";
|
|
4
|
+
import { downloadHtml, documentName } from "../../core/download.js";
|
|
5
|
+
import { keys, revealLabel } from "./preferences.js";
|
|
6
|
+
|
|
7
|
+
const RESUME_KEY = "menu-resume";
|
|
8
|
+
const READY_MS = 5000;
|
|
9
|
+
|
|
10
|
+
const clay = () => window.clay || {};
|
|
11
|
+
const session = () => clay().storage && clay().storage.session;
|
|
12
|
+
const pageWants = (name) => {
|
|
13
|
+
const names = clay().editPlugins;
|
|
14
|
+
return Array.isArray(names) && names.includes(name);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// An edit-only plugin attaches after the mode change commits, so the item waits for it
|
|
18
|
+
// briefly instead of reporting it missing.
|
|
19
|
+
function whenPresent(get, ms = READY_MS) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
const tick = () => {
|
|
23
|
+
const value = get();
|
|
24
|
+
if (value || Date.now() - start >= ms) resolve(value || null);
|
|
25
|
+
else setTimeout(tick, 50);
|
|
26
|
+
};
|
|
27
|
+
tick();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function attachActions({ addItem, note, currentMode }) {
|
|
32
|
+
const resumers = new Map();
|
|
33
|
+
|
|
34
|
+
// Runs `run` in edit mode, entering it first when the page is in view. A reload entry
|
|
35
|
+
// leaves a one-shot receipt in session storage so the action runs once the page is
|
|
36
|
+
// back; a refused entry says why and runs nothing.
|
|
37
|
+
async function inEditMode(action, run) {
|
|
38
|
+
if (currentMode() === "edit") return run();
|
|
39
|
+
const storage = session();
|
|
40
|
+
if (storage) storage.set(RESUME_KEY, action);
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = await clay().setMode?.("edit");
|
|
44
|
+
} catch (err) {
|
|
45
|
+
result = { ok: false, message: err && err.message };
|
|
46
|
+
}
|
|
47
|
+
if (result && result.ok !== false && result.reloading) return;
|
|
48
|
+
if (storage) storage.remove(RESUME_KEY);
|
|
49
|
+
if (!result || result.ok === false) {
|
|
50
|
+
const text = result ? modeRefusalText(result) : "Couldn't switch modes.";
|
|
51
|
+
if (text) note(text);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
return run();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resumable(action, run) {
|
|
58
|
+
resumers.set(action, run);
|
|
59
|
+
return () => inEditMode(action, run);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let history = { canUndo: false, canRedo: false };
|
|
63
|
+
let heard = false;
|
|
64
|
+
|
|
65
|
+
function renderUndo() {
|
|
66
|
+
if (!pageWants("undo")) return;
|
|
67
|
+
const live = currentMode() === "edit" && clay().undo;
|
|
68
|
+
// The menu loads late, so edits made before it are read from the service itself.
|
|
69
|
+
if (!heard && live && typeof live.canUndo === "boolean") history = { canUndo: live.canUndo, canRedo: !!live.canRedo };
|
|
70
|
+
addItem({
|
|
71
|
+
id: "clay.undo",
|
|
72
|
+
pair: [
|
|
73
|
+
{
|
|
74
|
+
id: "clay.undo.undo",
|
|
75
|
+
label: "Undo",
|
|
76
|
+
keys: keys("⌘Z", "Ctrl+Z"),
|
|
77
|
+
disabled: !(live && history.canUndo),
|
|
78
|
+
run: () => clay().undo?.undo(),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "clay.undo.redo",
|
|
82
|
+
label: "Redo",
|
|
83
|
+
keys: keys("⌘⇧Z", "Ctrl+Shift+Z"),
|
|
84
|
+
disabled: !(live && history.canRedo),
|
|
85
|
+
run: () => clay().undo?.redo(),
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
document.addEventListener("clay:undo-change", (event) => {
|
|
92
|
+
const detail = event.detail || {};
|
|
93
|
+
heard = true;
|
|
94
|
+
history = { canUndo: !!detail.canUndo, canRedo: !!detail.canRedo };
|
|
95
|
+
renderUndo();
|
|
96
|
+
});
|
|
97
|
+
document.addEventListener("clay:modechange", renderUndo);
|
|
98
|
+
renderUndo();
|
|
99
|
+
|
|
100
|
+
if (pageWants("ai-edit")) {
|
|
101
|
+
addItem({
|
|
102
|
+
id: "clay.ai",
|
|
103
|
+
label: "Ask AI…",
|
|
104
|
+
keys: keys("⌘J", "Ctrl+J"),
|
|
105
|
+
run: resumable("ai", async () => {
|
|
106
|
+
const ai = await whenPresent(() => clay().aiEdit);
|
|
107
|
+
const opened = ai ? await ai.open(document.body) : false;
|
|
108
|
+
if (!opened) note("Ask AI isn't available on this page right now.");
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (pageWants("cms") && clay().cms?.hasRules?.(document)) {
|
|
114
|
+
addItem({
|
|
115
|
+
id: "clay.cms",
|
|
116
|
+
label: "Open the CMS (Edit content)",
|
|
117
|
+
run: resumable("cms", async () => {
|
|
118
|
+
const cms = await whenPresent(() => clay().cms);
|
|
119
|
+
let result = null;
|
|
120
|
+
try {
|
|
121
|
+
result = cms ? await cms.open() : { ok: false };
|
|
122
|
+
} catch {
|
|
123
|
+
result = { ok: false };
|
|
124
|
+
}
|
|
125
|
+
if (result && result.ok === false) note("Couldn't open the CMS. Try again.");
|
|
126
|
+
}),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The copy is what a save would send now, unsaved edits included. A reader has no
|
|
131
|
+
// unsaved edits, so the saved file is the same document.
|
|
132
|
+
addItem({
|
|
133
|
+
id: "clay.download",
|
|
134
|
+
label: "Download a copy",
|
|
135
|
+
run: async () => {
|
|
136
|
+
let html = null;
|
|
137
|
+
try {
|
|
138
|
+
if (typeof clay().getHTML === "function") {
|
|
139
|
+
html = clay().getHTML();
|
|
140
|
+
} else {
|
|
141
|
+
const response = await fetch(location.href, { cache: "no-store", credentials: "same-origin" });
|
|
142
|
+
if (response.ok) html = await response.text();
|
|
143
|
+
}
|
|
144
|
+
} catch {}
|
|
145
|
+
// Upload paths only resolve on this host, so the copy names them in full.
|
|
146
|
+
if (typeof html === "string" && /^https?:$/.test(location.protocol)) {
|
|
147
|
+
html = html.replace(/(^|[^\w./:-])\/_\/uploads\//g, `$1${location.origin}/_/uploads/`);
|
|
148
|
+
}
|
|
149
|
+
if (typeof html !== "string" || !downloadHtml({ html, name: `${documentName()}.html` })) {
|
|
150
|
+
note("Couldn't build the download. Try again.");
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// What the host offers for this document. A page opens in a new tab so this one keeps
|
|
156
|
+
// whatever is unsaved; anything else the host does itself when asked, and may answer
|
|
157
|
+
// with a page to open (a duplicate) or a reason it could not.
|
|
158
|
+
async function askHost(url) {
|
|
159
|
+
let body = null;
|
|
160
|
+
try {
|
|
161
|
+
const response = await fetch(url, { method: "POST", credentials: "same-origin", headers: { "Document-URL": location.href } });
|
|
162
|
+
body = await response.json().catch(() => null);
|
|
163
|
+
if (response.ok && body?.ok !== false) {
|
|
164
|
+
if (typeof body?.href === "string") window.open(body.href, "_blank", "noopener");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
} catch {}
|
|
168
|
+
note(typeof body?.message === "string" && body.message ? body.message : "Couldn't do that. Try again.");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const hostItem = (id, label, action) => {
|
|
172
|
+
if (action?.href) addItem({ id, label, href: action.href });
|
|
173
|
+
else if (action?.post) addItem({ id, label, run: () => askHost(action.post) });
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
import("../../core/host-meta.js")
|
|
177
|
+
.then(({ hostActions }) => hostActions())
|
|
178
|
+
.then((host) => {
|
|
179
|
+
hostItem("clay.code", "Open the code editor", host.codeEditor);
|
|
180
|
+
hostItem("clay.duplicate", "Duplicate as a new page", host.duplicate);
|
|
181
|
+
hostItem("clay.export", "Export as zip", host.export);
|
|
182
|
+
hostItem("clay.reveal", revealLabel(), host.reveal);
|
|
183
|
+
hostItem("clay.versions", "Version history…", host.versions);
|
|
184
|
+
})
|
|
185
|
+
.catch(() => {});
|
|
186
|
+
|
|
187
|
+
const storage = session();
|
|
188
|
+
const pending = storage && storage.get(RESUME_KEY);
|
|
189
|
+
if (pending) {
|
|
190
|
+
storage.remove(RESUME_KEY);
|
|
191
|
+
const run = resumers.get(pending);
|
|
192
|
+
if (run && currentMode() === "edit") run();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { inEditMode, resumable, refresh: renderUndo };
|
|
196
|
+
}
|