@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
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* serialize-steps.js — the save's serializations as generators, for a document big enough
|
|
3
|
+
* to prepare its save across tasks.
|
|
4
|
+
*
|
|
5
|
+
* Each one returns exactly the string its one-call twin in snapshot.js returns, built child
|
|
6
|
+
* by child so it can yield between pieces. They live apart from snapshot.js because every
|
|
7
|
+
* page loads that at boot, and only the sliced save (core/save-job.js, a lazy chunk) and a
|
|
8
|
+
* sliced save's live-sync relay ever run these.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
clonePreventingOnclone,
|
|
13
|
+
noscriptBlocks,
|
|
14
|
+
prepareForCompare,
|
|
15
|
+
swapNoscriptMarkers
|
|
16
|
+
} from './snapshot.js';
|
|
17
|
+
import { TAB_LOCAL_ROOT_ATTRS } from '../lib/root-attrs.js';
|
|
18
|
+
|
|
19
|
+
const XHTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
20
|
+
|
|
21
|
+
// How much serialized output a sliced save writes before it hands the page back, and how
|
|
22
|
+
// big a subtree has to be before it is serialized child by child instead of in one call.
|
|
23
|
+
const SLICE_BYTES = 64 * 1024;
|
|
24
|
+
const SLICE_MIN_ELEMENTS = 256;
|
|
25
|
+
|
|
26
|
+
function sliceable(el) {
|
|
27
|
+
return el.namespaceURI === XHTML_NS
|
|
28
|
+
&& el.localName !== 'template'
|
|
29
|
+
&& el.getElementsByTagName('*').length >= SLICE_MIN_ELEMENTS;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The children of `el`, each one step, `yield`ing every `SLICE_BYTES` of output. A text
|
|
34
|
+
* or comment child is serialized inside `bare`, a childless copy of its parent, so its
|
|
35
|
+
* escaping follows the same parent element the one-call serializer would use.
|
|
36
|
+
*/
|
|
37
|
+
function* serializeChildrenSteps(el, bare, out, budget) {
|
|
38
|
+
for (const child of el.childNodes) {
|
|
39
|
+
if (child.nodeType === 1 && sliceable(child)) {
|
|
40
|
+
yield* serializeElementSteps(child, out, budget);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
let piece;
|
|
44
|
+
if (child.nodeType === 1) piece = child.outerHTML;
|
|
45
|
+
else {
|
|
46
|
+
bare.replaceChildren(child.cloneNode(false));
|
|
47
|
+
piece = bare.innerHTML;
|
|
48
|
+
}
|
|
49
|
+
out.push(piece);
|
|
50
|
+
budget.bytes += piece.length;
|
|
51
|
+
if (budget.bytes >= SLICE_BYTES) {
|
|
52
|
+
budget.bytes = 0;
|
|
53
|
+
yield;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* `el.outerHTML`, built from its open tag, each child's own serialization and its end
|
|
60
|
+
* tag, `yield`ing every `SLICE_BYTES` of output.
|
|
61
|
+
*/
|
|
62
|
+
function* serializeElementSteps(el, out, budget) {
|
|
63
|
+
const bare = clonePreventingOnclone(el, false);
|
|
64
|
+
const shell = bare.outerHTML;
|
|
65
|
+
const endTag = `</${bare.localName}>`;
|
|
66
|
+
out.push(shell.slice(0, shell.length - endTag.length));
|
|
67
|
+
yield* serializeChildrenSteps(el, bare, out, budget);
|
|
68
|
+
out.push(endTag);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** `"<!DOCTYPE html>" + root.outerHTML`, in steps. */
|
|
72
|
+
export function* serializeTreeSteps(root) {
|
|
73
|
+
const out = ["<!DOCTYPE html>"];
|
|
74
|
+
yield* serializeElementSteps(root, out, { bytes: 0 });
|
|
75
|
+
return out.join('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** `serializeClone`, in steps: the same bytes, `yield`ing every `SLICE_BYTES` of output. */
|
|
79
|
+
export function* serializeCloneSteps(clone) {
|
|
80
|
+
const blocks = noscriptBlocks(clone);
|
|
81
|
+
if (!blocks.length) return yield* serializeTreeSteps(clone);
|
|
82
|
+
const raw = swapNoscriptMarkers(blocks);
|
|
83
|
+
let html;
|
|
84
|
+
try {
|
|
85
|
+
html = yield* serializeTreeSteps(clone);
|
|
86
|
+
} finally {
|
|
87
|
+
for (const [el, kids] of blocks) el.replaceChildren(...kids);
|
|
88
|
+
}
|
|
89
|
+
for (const [marker, text] of raw) html = html.split(marker).join(text);
|
|
90
|
+
return html;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** `serializeForCompare`, in steps. */
|
|
94
|
+
export function* serializeForCompareSteps(clone) {
|
|
95
|
+
prepareForCompare(clone);
|
|
96
|
+
return yield* serializeTreeSteps(clone);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** `serializeForSync`, in steps: the same bytes, `yield`ing every `SLICE_BYTES` of output. */
|
|
100
|
+
export function* serializeForSyncSteps(clone) {
|
|
101
|
+
const bareRoot = clonePreventingOnclone(clone, false);
|
|
102
|
+
for (const name of TAB_LOCAL_ROOT_ATTRS) bareRoot.removeAttribute(name);
|
|
103
|
+
const shell = bareRoot.outerHTML;
|
|
104
|
+
const endTag = `</${bareRoot.localName}>`;
|
|
105
|
+
const out = ["<!DOCTYPE html>", shell.slice(0, shell.length - endTag.length)];
|
|
106
|
+
yield* serializeChildrenSteps(clone, clonePreventingOnclone(clone, false), out, { bytes: 0 });
|
|
107
|
+
out.push(endTag);
|
|
108
|
+
return out.join('');
|
|
109
|
+
}
|
package/src/core/snapshot.js
CHANGED
|
@@ -58,6 +58,7 @@ const snapshotProvenance = new WeakMap();
|
|
|
58
58
|
// because many things want a turn; serializing the document is one decision, so a
|
|
59
59
|
// second renderer would be two answers to "what bytes does this save send".
|
|
60
60
|
let saveRenderer = null;
|
|
61
|
+
let saveRendererSteps = null;
|
|
61
62
|
|
|
62
63
|
// The capture identity for save captures. A refused save that may retry reads it
|
|
63
64
|
// before sending and again before each retry: a capture newer than the one those
|
|
@@ -105,6 +106,37 @@ export function attachCaptureArtifact(artifact) {
|
|
|
105
106
|
if (activeCapture && artifact) activeCapture.artifact = artifact;
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
// The page state of one save capture: the exact value its page tag carried when its
|
|
110
|
+
// bytes were serialized, copied so the edits that follow the capture can never reach it.
|
|
111
|
+
// Keyed by the capture id every accepted event carries, and dropped when the capture
|
|
112
|
+
// settles, so an accepted save reads the state it was captured from rather than the
|
|
113
|
+
// store's value at the moment the host answered.
|
|
114
|
+
let capturePageStateReader = null;
|
|
115
|
+
const capturePageStates = new Map();
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Register the reader that finds a page state in a final save clone.
|
|
119
|
+
*
|
|
120
|
+
* A page's own code registers it (page/save-projection.js), so a document without a
|
|
121
|
+
* page pays one null check per capture and nothing else. Read from the clone rather
|
|
122
|
+
* than from a store because that tag is what the file receives, after every transform
|
|
123
|
+
* and every authored save handler has run.
|
|
124
|
+
*/
|
|
125
|
+
export function setCapturePageState(reader) {
|
|
126
|
+
capturePageStateReader = reader;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The page state pinned to one capture, or null.
|
|
131
|
+
*
|
|
132
|
+
* @param {?number} captureId
|
|
133
|
+
* @returns {?Object}
|
|
134
|
+
*/
|
|
135
|
+
export function capturePageState(captureId) {
|
|
136
|
+
if (captureId === null || captureId === undefined) return null;
|
|
137
|
+
return capturePageStates.get(captureId) ?? null;
|
|
138
|
+
}
|
|
139
|
+
|
|
108
140
|
/** The artifact this capture's renderer attached, or null. */
|
|
109
141
|
export function captureArtifact(result) {
|
|
110
142
|
return result ? captureArtifacts.get(result) || null : null;
|
|
@@ -112,7 +144,9 @@ export function captureArtifact(result) {
|
|
|
112
144
|
|
|
113
145
|
/** Forget a capture's artifact: it was skipped, refused, or has now settled. */
|
|
114
146
|
export function releaseCaptureArtifact(result) {
|
|
115
|
-
if (result)
|
|
147
|
+
if (!result) return;
|
|
148
|
+
captureArtifacts.delete(result);
|
|
149
|
+
capturePageStates.delete(result.captureId);
|
|
116
150
|
}
|
|
117
151
|
|
|
118
152
|
/**
|
|
@@ -204,10 +238,16 @@ export function onSnapshot(callback) {
|
|
|
204
238
|
* strings are only ever compared against each other, so they gain nothing from
|
|
205
239
|
* source fidelity and would pay a full render on every keystroke's dirty check.
|
|
206
240
|
*
|
|
241
|
+
* `steps` is the same render as a generator, for a save that verifies across tasks; a
|
|
242
|
+
* renderer without one is called synchronously at that point.
|
|
243
|
+
*
|
|
207
244
|
* @param {?Function} renderer - (clone, today) => string, or null to restore outerHTML
|
|
245
|
+
* @param {Object} [options]
|
|
246
|
+
* @param {?Function} [options.steps] - (clone, today) => generator
|
|
208
247
|
*/
|
|
209
|
-
export function setSaveRenderer(renderer) {
|
|
248
|
+
export function setSaveRenderer(renderer, { steps = null } = {}) {
|
|
210
249
|
saveRenderer = renderer;
|
|
250
|
+
saveRendererSteps = steps;
|
|
211
251
|
}
|
|
212
252
|
|
|
213
253
|
const XHTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
@@ -222,7 +262,22 @@ const XHTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
|
222
262
|
* reads as literal text, and every later save escaped it again. The text is swapped for
|
|
223
263
|
* a marker only while serializing, so the clone holds the same nodes when this returns.
|
|
224
264
|
*/
|
|
225
|
-
function serializeClone(clone) {
|
|
265
|
+
export function serializeClone(clone) {
|
|
266
|
+
const blocks = noscriptBlocks(clone);
|
|
267
|
+
if (!blocks.length) return "<!DOCTYPE html>" + clone.outerHTML;
|
|
268
|
+
const raw = swapNoscriptMarkers(blocks);
|
|
269
|
+
let html;
|
|
270
|
+
try {
|
|
271
|
+
html = "<!DOCTYPE html>" + clone.outerHTML;
|
|
272
|
+
} finally {
|
|
273
|
+
for (const [el, kids] of blocks) el.replaceChildren(...kids);
|
|
274
|
+
}
|
|
275
|
+
for (const [marker, text] of raw) html = html.split(marker).join(text);
|
|
276
|
+
return html;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Every <noscript> in the tree whose content is text only, with the children it held. */
|
|
280
|
+
export function noscriptBlocks(clone) {
|
|
226
281
|
const blocks = [];
|
|
227
282
|
const collect = (node) => {
|
|
228
283
|
for (const child of node.childNodes) {
|
|
@@ -236,21 +291,34 @@ function serializeClone(clone) {
|
|
|
236
291
|
}
|
|
237
292
|
};
|
|
238
293
|
collect(clone);
|
|
239
|
-
|
|
294
|
+
return blocks;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Swap each block's text for a marker, and answer the text each marker stands for. */
|
|
298
|
+
export function swapNoscriptMarkers(blocks) {
|
|
240
299
|
const nonce = Math.random().toString(36).slice(2);
|
|
241
|
-
|
|
300
|
+
return blocks.map(([el, kids], i) => {
|
|
242
301
|
const marker = `\uE000clay-noscript-${nonce}-${i}\uE000`;
|
|
243
302
|
el.replaceChildren(el.ownerDocument.createTextNode(marker));
|
|
244
303
|
return [marker, kids.map((n) => n.data).join('')];
|
|
245
304
|
});
|
|
246
|
-
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Run one resume of a save renderer with `context` as the capture being serialized.
|
|
309
|
+
*
|
|
310
|
+
* The capture is never the active one while the task that is not running it looks: a
|
|
311
|
+
* renderer that verifies across tasks is resumed once per task, and each resume is its
|
|
312
|
+
* own call in here.
|
|
313
|
+
*/
|
|
314
|
+
export function runSaveRenderer(context, resume) {
|
|
315
|
+
const previous = activeCapture;
|
|
316
|
+
activeCapture = context;
|
|
247
317
|
try {
|
|
248
|
-
|
|
318
|
+
return resume();
|
|
249
319
|
} finally {
|
|
250
|
-
|
|
320
|
+
activeCapture = previous;
|
|
251
321
|
}
|
|
252
|
-
for (const [marker, text] of raw) html = html.split(marker).join(text);
|
|
253
|
-
return html;
|
|
254
322
|
}
|
|
255
323
|
|
|
256
324
|
/**
|
|
@@ -263,17 +331,18 @@ function serializeClone(clone) {
|
|
|
263
331
|
* cost somebody a save.
|
|
264
332
|
*/
|
|
265
333
|
function serializeSaveClone(clone, context = null) {
|
|
334
|
+
if (context && capturePageStateReader) {
|
|
335
|
+
let state = null;
|
|
336
|
+
try { state = capturePageStateReader(clone); } catch (_) {}
|
|
337
|
+
if (state) capturePageStates.set(context.captureId, state);
|
|
338
|
+
}
|
|
266
339
|
const today = serializeClone(clone);
|
|
267
340
|
if (!saveRenderer) return today;
|
|
268
|
-
const previous = activeCapture;
|
|
269
|
-
activeCapture = context;
|
|
270
341
|
try {
|
|
271
|
-
return saveRenderer(clone, today);
|
|
342
|
+
return runSaveRenderer(context, () => saveRenderer(clone, today));
|
|
272
343
|
} catch (err) {
|
|
273
344
|
console.error('clayjs: save renderer failed, sending the full serialization', err);
|
|
274
345
|
return today;
|
|
275
|
-
} finally {
|
|
276
|
-
activeCapture = previous;
|
|
277
346
|
}
|
|
278
347
|
}
|
|
279
348
|
|
|
@@ -305,13 +374,26 @@ export function addDocumentTransform(callback) {
|
|
|
305
374
|
*
|
|
306
375
|
* @returns {HTMLElement} Cloned document element with snapshot hooks applied
|
|
307
376
|
*/
|
|
308
|
-
function clonePreventingOnclone(node, deep = true) {
|
|
377
|
+
export function clonePreventingOnclone(node, deep = true) {
|
|
309
378
|
const prev = window.__preventOnclone;
|
|
310
379
|
window.__preventOnclone = true;
|
|
311
380
|
try { return node.cloneNode(deep); }
|
|
312
381
|
finally { window.__preventOnclone = prev; }
|
|
313
382
|
}
|
|
314
383
|
|
|
384
|
+
// A clone OF the save clone (the comparison and dirty trees) is not the page, so the
|
|
385
|
+
// view recorded no provenance for it: a transform that asks originalSnapshotNode would
|
|
386
|
+
// get null there and answer differently than on the save clone it came from. The two
|
|
387
|
+
// trees are isomorphic when this runs, so a lockstep walk registers each clone node
|
|
388
|
+
// against the original its save-clone twin maps to.
|
|
389
|
+
function carryProvenance(saveNode, cloneNode) {
|
|
390
|
+
const live = snapshotProvenance.get(saveNode);
|
|
391
|
+
if (live) snapshotProvenance.set(cloneNode, live);
|
|
392
|
+
const saveKids = saveNode.children;
|
|
393
|
+
const cloneKids = cloneNode.children;
|
|
394
|
+
for (let i = 0; i < saveKids.length; i++) carryProvenance(saveKids[i], cloneKids[i]);
|
|
395
|
+
}
|
|
396
|
+
|
|
315
397
|
export function captureSnapshot({ flushUndo = true, authored = true, forSave = false, requestRefresh = false, context = null } = {}) {
|
|
316
398
|
// Force-close any pending undo idle batch BEFORE cloning the DOM, so the
|
|
317
399
|
// snapshot reflects a clean undo boundary. Without this, a save that fires
|
|
@@ -435,12 +517,16 @@ export function captureSaveClone({ flushUndo = true, authored = true } = {}) {
|
|
|
435
517
|
// check must not read that as an edit the person would lose on close.
|
|
436
518
|
const EMPTY_INERT_ATTRS = ['style', 'class'];
|
|
437
519
|
|
|
438
|
-
function
|
|
520
|
+
export function prepareForCompare(clone) {
|
|
439
521
|
stripDerived(clone);
|
|
440
522
|
for (const name of EMPTY_INERT_ATTRS) {
|
|
441
523
|
if (clone.getAttribute(name) === '') clone.removeAttribute(name);
|
|
442
524
|
for (const el of clone.querySelectorAll(`[${name}=""]`)) el.removeAttribute(name);
|
|
443
525
|
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export function serializeForCompare(clone) {
|
|
529
|
+
prepareForCompare(clone);
|
|
444
530
|
return "<!DOCTYPE html>" + clone.outerHTML;
|
|
445
531
|
}
|
|
446
532
|
|
|
@@ -527,6 +613,7 @@ export function captureForComparisonAndDirty({ flushUndo = true } = {}) {
|
|
|
527
613
|
const dirtyClone = clone.querySelector(NO_TRIGGER_AUTOSAVE_SELECTOR)
|
|
528
614
|
? clonePreventingOnclone(clone)
|
|
529
615
|
: null;
|
|
616
|
+
if (dirtyClone) carryProvenance(clone, dirtyClone);
|
|
530
617
|
|
|
531
618
|
for (const el of clone.querySelectorAll(STRIP_FROM_COMPARISON)) {
|
|
532
619
|
el.remove();
|
|
@@ -555,23 +642,32 @@ export function captureForComparisonAndDirty({ flushUndo = true } = {}) {
|
|
|
555
642
|
}
|
|
556
643
|
|
|
557
644
|
/**
|
|
558
|
-
*
|
|
645
|
+
* Take one save capture's looking at the live page: in one synchronous call, everything
|
|
646
|
+
* that must see it (the snapshot, its hooks, snapshot-ready, the authored handlers, the
|
|
647
|
+
* comparison clones and every transform over all three).
|
|
559
648
|
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
649
|
+
* What comes back is the pin the rest of the capture reads: every value that work needs,
|
|
650
|
+
* so a page edited afterwards cannot change the answer. It is the one place the strip and
|
|
651
|
+
* transform rules live, for both the one-task capture and a job that finishes across
|
|
652
|
+
* tasks — core/save-job.js, which only a save that prepares across tasks loads.
|
|
562
653
|
*
|
|
563
654
|
* @param {Object} options
|
|
564
655
|
* @param {boolean} options.emitForSync - Whether to emit snapshot-ready event (default: true)
|
|
565
656
|
* @param {boolean} options.forRequest - These bytes go to a host, so the renderer may
|
|
566
657
|
* attach the verified model they came from, for the accepted save to adopt
|
|
567
|
-
* @
|
|
658
|
+
* @param {?{name: string, content: string}} options.pageFormatMarker - The document format
|
|
659
|
+
* these bytes must declare, as the attributes of the marker element. It goes into the
|
|
660
|
+
* save clone after the comparison domains are read and before the renderer verifies, so
|
|
661
|
+
* the bytes verified are the bytes sent; the comparison baselines stay marker-free,
|
|
662
|
+
* exactly as they do on the one-task path
|
|
663
|
+
* @returns {Object} the pin `job.steps()` and the one-task capture both read
|
|
568
664
|
*/
|
|
569
|
-
export function
|
|
665
|
+
export function prepareSaveCapture({ emitForSync = true, flushUndo = true, requestRefresh = false, forRequest = false, pageFormatMarker = null, sliced = false } = {}) {
|
|
570
666
|
if (!Number.isSafeInteger(captureCounter + 1)) throw new Error('Save capture identity exhausted');
|
|
571
667
|
const captureId = ++captureCounter;
|
|
572
668
|
// The context this capture's renderer may attach a private artifact to, and the
|
|
573
669
|
// record of whether the page has its own save handlers. It never reaches the object
|
|
574
|
-
//
|
|
670
|
+
// the capture returns: that keeps its four public fields.
|
|
575
671
|
//
|
|
576
672
|
// Only for a capture whose bytes go to a host. A capture taken for anything else (a
|
|
577
673
|
// live-sync disk base, a download, an inspection) has no request to adopt it, so it
|
|
@@ -581,14 +677,12 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
581
677
|
|
|
582
678
|
// Emit for live-sync before any stripping.
|
|
583
679
|
//
|
|
584
|
-
// A listener gets the very clone this
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
// to it: anything it changes lands in the saved bytes and in both baselines,
|
|
588
|
-
// and a baseline the dirty check cannot reproduce warns on close forever.
|
|
680
|
+
// A listener gets the very clone this capture goes on to serialize into forSave,
|
|
681
|
+
// forComparison and forDirty — that sharing is the point, it saves a second full DOM
|
|
682
|
+
// clone per save. So a listener may READ it and must not write to it.
|
|
589
683
|
if (emitForSync) {
|
|
590
684
|
document.dispatchEvent(new CustomEvent('clay:snapshot-ready', {
|
|
591
|
-
detail: { documentElement: clone, forSave: true, captureId }
|
|
685
|
+
detail: { documentElement: clone, forSave: true, captureId, sliced }
|
|
592
686
|
}));
|
|
593
687
|
}
|
|
594
688
|
|
|
@@ -597,15 +691,14 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
597
691
|
|
|
598
692
|
// Clone for comparison before stripping (cheaper than cloning live DOM)
|
|
599
693
|
const compareClone = clonePreventingOnclone(clone);
|
|
694
|
+
carryProvenance(clone, compareClone);
|
|
600
695
|
|
|
601
|
-
// The dirty domain differs from the autosave domain only inside
|
|
602
|
-
//
|
|
603
|
-
// strip sets remove exactly the same nodes and one string serves both. The
|
|
604
|
-
// predicate reads the captured clone after authored handlers have run, and
|
|
605
|
-
// covers all three spellings via the policy's own selector.
|
|
696
|
+
// The dirty domain differs from the autosave domain only inside no-trigger-autosave
|
|
697
|
+
// regions, so a document without one pays nothing: one string serves both.
|
|
606
698
|
const dirtyClone = clone.querySelector(NO_TRIGGER_AUTOSAVE_SELECTOR)
|
|
607
699
|
? clonePreventingOnclone(clone)
|
|
608
700
|
: null;
|
|
701
|
+
if (dirtyClone) carryProvenance(clone, dirtyClone);
|
|
609
702
|
|
|
610
703
|
// Save clone: run hooks (freeze restore lives here), THEN strip [no-save]/[save-remove]
|
|
611
704
|
// LAST (snapshot-algorithm step 7) so freeze-restored [no-save] content can't leak to disk.
|
|
@@ -618,7 +711,6 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
618
711
|
el.remove();
|
|
619
712
|
}
|
|
620
713
|
stripDiagnosticBeacons(clone);
|
|
621
|
-
const forSave = serializeSaveClone(clone, context);
|
|
622
714
|
|
|
623
715
|
// Compare clone: strip every autosave-off region, then run hooks
|
|
624
716
|
for (const el of compareClone.querySelectorAll(STRIP_FROM_COMPARISON)) {
|
|
@@ -629,10 +721,8 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
629
721
|
hook(compareClone);
|
|
630
722
|
}
|
|
631
723
|
stripSnapshotRegions(compareClone);
|
|
632
|
-
const forComparison = serializeForCompare(compareClone);
|
|
633
724
|
|
|
634
725
|
// Dirty clone: same shape as the compare clone, one selector weaker.
|
|
635
|
-
let forDirty = forComparison;
|
|
636
726
|
if (dirtyClone) {
|
|
637
727
|
for (const el of dirtyClone.querySelectorAll(STRIP_FROM_DIRTY_CHECK)) {
|
|
638
728
|
el.remove();
|
|
@@ -642,14 +732,88 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
642
732
|
hook(dirtyClone);
|
|
643
733
|
}
|
|
644
734
|
stripSnapshotRegions(dirtyClone);
|
|
645
|
-
forDirty = serializeForCompare(dirtyClone);
|
|
646
735
|
}
|
|
647
736
|
|
|
648
|
-
|
|
649
|
-
|
|
737
|
+
return {
|
|
738
|
+
captureId,
|
|
739
|
+
context,
|
|
740
|
+
pageFormatMarker,
|
|
741
|
+
clone,
|
|
742
|
+
compareClone,
|
|
743
|
+
dirtyClone,
|
|
744
|
+
renderer: saveRenderer,
|
|
745
|
+
rendererSteps: saveRendererSteps,
|
|
746
|
+
pageStateReader: capturePageStateReader,
|
|
747
|
+
result: null,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Copy the page state this capture's bytes carry, before anything edits the clone.
|
|
753
|
+
*
|
|
754
|
+
* A capture taken for a request records the exact value its page tag carried, so the save
|
|
755
|
+
* the host accepts reads the state these bytes describe rather than the store's value at
|
|
756
|
+
* the moment the host answered.
|
|
757
|
+
*/
|
|
758
|
+
export function readCapturePageState(pin) {
|
|
759
|
+
if (!pin.context || !pin.pageStateReader) return;
|
|
760
|
+
let state = null;
|
|
761
|
+
try { state = pin.pageStateReader(pin.clone); } catch (_) {}
|
|
762
|
+
if (state) capturePageStates.set(pin.captureId, state);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Finish a save capture: the result object, and the artifact its renderer attached.
|
|
767
|
+
*
|
|
768
|
+
* @returns {{ forSave: string, forComparison: string, forDirty: string, captureId: number }}
|
|
769
|
+
*/
|
|
770
|
+
export function completeSaveCapture(pin, { forSave, forComparison, forDirty }) {
|
|
771
|
+
const result = { forSave, forComparison, forDirty, captureId: pin.captureId };
|
|
772
|
+
if (pin.context && pin.context.artifact) captureArtifacts.set(result, pin.context.artifact);
|
|
773
|
+
pin.result = result;
|
|
650
774
|
return result;
|
|
651
775
|
}
|
|
652
776
|
|
|
777
|
+
/** Give back everything a capture holds. Idempotent. */
|
|
778
|
+
export function releaseSaveCapture(pin) {
|
|
779
|
+
releaseCaptureArtifact(pin.result);
|
|
780
|
+
capturePageStates.delete(pin.captureId);
|
|
781
|
+
if (pin.context) pin.context.artifact = null;
|
|
782
|
+
pin.clone = pin.compareClone = pin.dirtyClone = null;
|
|
783
|
+
pin.result = null;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Single-capture function for both saving and comparison.
|
|
788
|
+
*
|
|
789
|
+
* Clones the DOM once, then clones that clone for comparison.
|
|
790
|
+
* More efficient than calling captureForSave() and captureForComparison() separately.
|
|
791
|
+
*
|
|
792
|
+
* The one-task form of a save capture: the same preparation, the same bytes and the same
|
|
793
|
+
* release as a job drained in the caller's own task, written out rather than resumed so
|
|
794
|
+
* nothing a page with small documents never uses has to be loaded to save it.
|
|
795
|
+
*
|
|
796
|
+
* @param {Object} options
|
|
797
|
+
* @param {boolean} options.emitForSync - Whether to emit snapshot-ready event (default: true)
|
|
798
|
+
* @param {boolean} options.forRequest - These bytes go to a host, so the renderer may
|
|
799
|
+
* attach the verified model they came from, for the accepted save to adopt
|
|
800
|
+
* @returns {{ forSave: string, forComparison: string, forDirty: string, captureId: number }}
|
|
801
|
+
*/
|
|
802
|
+
export function captureForSaveAndComparison(opts = {}) {
|
|
803
|
+
const pin = prepareSaveCapture(opts);
|
|
804
|
+
try {
|
|
805
|
+
readCapturePageState(pin);
|
|
806
|
+
const today = serializeClone(pin.clone);
|
|
807
|
+
const forComparison = pin.compareClone ? serializeForCompare(pin.compareClone) : null;
|
|
808
|
+
const forDirty = pin.dirtyClone ? serializeForCompare(pin.dirtyClone) : forComparison;
|
|
809
|
+
const forSave = pin.renderer ? runSaveRenderer(pin.context, () => pin.renderer(pin.clone, today)) : today;
|
|
810
|
+
return completeSaveCapture(pin, { forSave, forComparison, forDirty });
|
|
811
|
+
} catch (err) {
|
|
812
|
+
releaseSaveCapture(pin);
|
|
813
|
+
throw err;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
653
817
|
/**
|
|
654
818
|
* Capture for a protected live-sync merge: the save-domain and loss-domain
|
|
655
819
|
* clones of ONE snapshot, plus a WeakMap pairing every loss-domain element to
|
|
@@ -784,6 +948,11 @@ export function serializeForSync(clone) {
|
|
|
784
948
|
return "<!DOCTYPE html>" + shell.slice(0, shell.length - endTag.length) + clone.innerHTML + endTag;
|
|
785
949
|
}
|
|
786
950
|
|
|
951
|
+
/** A private deep copy of a capture's clone, for work that outlives the capture event. */
|
|
952
|
+
export function copyCaptureClone(clone) {
|
|
953
|
+
return clonePreventingOnclone(clone);
|
|
954
|
+
}
|
|
955
|
+
|
|
787
956
|
/**
|
|
788
957
|
* PHASE 1-2 (body only): For live-sync between admin users.
|
|
789
958
|
*
|