@panphora/clayjs 1.17.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 +72474 -62090
- package/dist/m/attrs/{save-freeze-MYNVXREN.js → save-freeze-LWGF6XUN.js} +1 -1
- package/dist/m/c-2GRUM77Q.js +30 -0
- package/dist/m/c-2KIEGNC4.js +1 -0
- package/dist/m/{c-AMSNPOCX.js → c-2KL3BD52.js} +1 -1
- 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-KAU6E43D.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-3XIRD4NU.js → c-5T6NZHNM.js} +1 -1
- package/dist/m/{c-74MGYWZF.js → c-5ZQV7KC4.js} +1 -1
- package/dist/m/{c-7OLLJPWP.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-Y5HBBIQ4.js → c-AMYEFA2E.js} +1 -1
- package/dist/m/c-AQYCG7QW.js +1 -0
- package/dist/m/c-ATKAOH23.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-VHM5XQHK.js → c-BIJALSRD.js} +1 -1
- package/dist/m/c-BJ7YQ5QZ.js +1 -0
- package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
- package/dist/m/{c-VE6ZMYMC.js → c-BUAYFEV5.js} +1 -1
- package/dist/m/c-C6IPRG34.js +1 -0
- package/dist/m/{c-KU3ZFADW.js → c-CAS3NND4.js} +1 -1
- package/dist/m/{c-PS5ARVWR.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-QJ4OM3G6.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-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-TJ6WOJL3.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-WDC5GVKU.js → c-IVTRVENZ.js} +1 -1
- package/dist/m/c-JKZJIUIP.js +1 -0
- package/dist/m/{c-UDUUJPFZ.js → c-JNLX3RJB.js} +1 -1
- package/dist/m/c-K3ADYNLU.js +1 -0
- package/dist/m/c-KGHVFRR3.js +1 -0
- package/dist/m/{c-5QWQ7A5L.js → c-KJJPV77R.js} +1 -1
- package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
- package/dist/m/{c-EZU56NQV.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-3RUU4TPX.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-5RJFTLSF.js → c-NEL7HQW3.js} +1 -1
- package/dist/m/{c-DCXOXBF3.js → c-NQJRVJ56.js} +1 -1
- 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-R3L7ZJGB.js → c-OPLUZR3A.js} +1 -1
- package/dist/m/{c-GSFIGKOP.js → c-OTMKN2OO.js} +1 -1
- package/dist/m/{c-6NC5I2TV.js → c-OYRZ7XG2.js} +1 -1
- package/dist/m/{c-EVCQ3BKA.js → c-P3OFYUMA.js} +1 -1
- 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-FRLHSY7E.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-JU3NK25H.js → c-R27DWQKX.js} +1 -1
- package/dist/m/{c-RSMOODDU.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-Q622NS2Y.js → c-S2U6NJU4.js} +1 -1
- package/dist/m/c-SPZPGD3R.js +1 -0
- package/dist/m/c-SW4YEG2D.js +8 -0
- package/dist/m/c-T637T7SN.js +1 -0
- package/dist/m/c-TJAXGIRV.js +1 -0
- package/dist/m/{c-RYUXUUBX.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-QICP2YR5.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-VILENZTD.js +1 -0
- package/dist/m/{c-HDDEEL77.js → c-VJYEVRYK.js} +1 -1
- package/dist/m/{c-SMBZG5X4.js → c-VR2BJ4AN.js} +1 -1
- 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-I2ZATQCD.js → c-X5TSXZYJ.js} +2 -2
- 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/core/{admin-attrs-QHOKPJ7W.js → admin-attrs-YY7T5FGU.js} +1 -1
- 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-FNXVEFNE.js → host-permission-6EVJGNPL.js} +1 -1
- package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
- package/dist/m/core/{persist-JFYRHMRJ.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/internals/{index-PVXGPUDA.js → index-TGLQINN3.js} +1 -1
- 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 +2111 -1573
- package/dist/m/page/{editor-NNJA4OKD.js → editor-6LRB3GE4.js} +123 -21
- 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-RM5IR6AO.js → demo-OYO5GCAO.js} +1 -1
- 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-SGPJKANK.js → people-JCHVPF7P.js} +1 -1
- package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
- package/dist/m/plugins/{richclay-NRQDSWX6.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-WLYKK6TL.js → upload-NHGJXZK2.js} +1 -1
- 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-4N6GYAF6.js → wire-VBWXEXU2.js} +1 -1
- 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 +1367 -930
- package/dist/page/themes/light-1.css +8 -7
- package/dist/server/core.cjs +1440 -288
- 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/conflict-notice-ui.js +4 -7
- package/src/core/download.js +2 -2
- package/src/core/host-meta.js +38 -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 +106 -31
- 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 +170 -39
- 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/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 +89 -27
- package/src/page/boot-pack.js +1 -1
- package/src/page/chart-tables.js +21 -3
- package/src/page/chart.js +406 -63
- package/src/page/checklist.js +10 -135
- package/src/page/checkpoint-proof.js +7 -1
- package/src/page/chrome.js +250 -51
- package/src/page/clipboard-shape.js +126 -0
- package/src/page/drag.js +33 -3
- package/src/page/editor.js +136 -25
- 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/checklist.js +428 -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/registry.js +18 -0
- package/src/page/features/table.js +373 -83
- 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 +25 -1
- package/src/page/format-gate.js +68 -18
- package/src/page/format-host.js +17 -0
- package/src/page/format-native-commands.js +21 -5
- package/src/page/image-actions.js +64 -0
- package/src/page/import-markdown.js +262 -0
- package/src/page/items.js +135 -17
- package/src/page/markdown.js +6 -2
- package/src/page/media.js +35 -119
- package/src/page/placeholder.js +14 -1
- package/src/page/projection.js +98 -10
- 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 +28 -0
- package/src/page/save-states.js +13 -3
- package/src/page/schema.js +19 -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/light-1.css +8 -7
- 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 +38 -12
- package/src/page/view.js +13 -0
- package/src/page/ybind/binding.js +88 -5
- 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 +1 -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 +113 -17
- package/src/page/ybind/recovery-proof.js +32 -9
- package/src/page/ybind/recovery-replay.js +558 -15
- 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 +89 -14
- 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 +249 -44
- 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 +5 -3
- 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/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 +11 -6
- 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/c-2SPVQSHC.js +0 -1
- package/dist/m/c-336SGU5W.js +0 -1
- package/dist/m/c-3H6BZDNJ.js +0 -6
- package/dist/m/c-4DFEJ2PO.js +0 -3
- package/dist/m/c-56QRZTZH.js +0 -1
- package/dist/m/c-5J4E7O4Y.js +0 -4
- package/dist/m/c-5LKDWQTL.js +0 -19
- package/dist/m/c-5V3MOVJT.js +0 -1
- package/dist/m/c-6AZOGAPE.js +0 -1
- package/dist/m/c-6CRUUXOX.js +0 -5
- package/dist/m/c-6HMB24EV.js +0 -1
- package/dist/m/c-6KEVEQ7L.js +0 -1
- package/dist/m/c-A3E3NIMC.js +0 -1
- package/dist/m/c-BQ6LOBEE.js +0 -1
- package/dist/m/c-BTDI4Y23.js +0 -1
- package/dist/m/c-CJ3A7ZKE.js +0 -1
- package/dist/m/c-CQHPGKX2.js +0 -1
- package/dist/m/c-DFS4XZJV.js +0 -2
- package/dist/m/c-DNENWY4O.js +0 -1
- package/dist/m/c-E4LIR5X5.js +0 -1
- package/dist/m/c-FDNHHZY5.js +0 -1
- package/dist/m/c-FROJUZMV.js +0 -1
- package/dist/m/c-G2JPKCTP.js +0 -1
- package/dist/m/c-GB6TU7WV.js +0 -1
- package/dist/m/c-GFJJEBTU.js +0 -1
- package/dist/m/c-GJAEGGXY.js +0 -1
- package/dist/m/c-GOWBDM6J.js +0 -1
- package/dist/m/c-HFKUVACH.js +0 -1
- package/dist/m/c-HHDFCNHV.js +0 -1
- package/dist/m/c-IEM4NTCS.js +0 -1
- package/dist/m/c-JDCX26UP.js +0 -25
- package/dist/m/c-KGVGROZT.js +0 -1
- package/dist/m/c-KQ6TYJAP.js +0 -135
- package/dist/m/c-LFPHCXY3.js +0 -1
- package/dist/m/c-LRPUO2MX.js +0 -1
- package/dist/m/c-LWLBHLVC.js +0 -3
- package/dist/m/c-M2L47S2X.js +0 -7
- package/dist/m/c-MFP2ZWD4.js +0 -1
- package/dist/m/c-MKSQ4Y57.js +0 -1
- package/dist/m/c-NUFFJVVJ.js +0 -1
- package/dist/m/c-NUHSPT5P.js +0 -1
- package/dist/m/c-O72HVYSA.js +0 -1
- package/dist/m/c-OIBUTDXI.js +0 -1
- package/dist/m/c-OW5QIGG3.js +0 -1
- package/dist/m/c-PGRCK73F.js +0 -1
- package/dist/m/c-Q7NQD65V.js +0 -1
- package/dist/m/c-QH63LQOS.js +0 -1
- package/dist/m/c-R7IWVINQ.js +0 -1
- package/dist/m/c-RM7J7BV2.js +0 -1
- package/dist/m/c-S4ZEH46B.js +0 -9
- package/dist/m/c-SDAXDM2D.js +0 -1
- package/dist/m/c-SMGU7RT3.js +0 -235
- package/dist/m/c-SPIORRYS.js +0 -1
- package/dist/m/c-SWLRT66R.js +0 -1
- package/dist/m/c-TCJTVAFN.js +0 -1
- package/dist/m/c-TQEMVJCS.js +0 -1
- package/dist/m/c-TYLXX4NF.js +0 -21
- package/dist/m/c-U62OB4BT.js +0 -1
- package/dist/m/c-U6YWUAR3.js +0 -8
- package/dist/m/c-UDCSU5G5.js +0 -2
- package/dist/m/c-VI5RMSVY.js +0 -1
- package/dist/m/c-VRZN56XY.js +0 -1
- package/dist/m/c-W5MCHEGA.js +0 -1
- package/dist/m/c-WNYBLIOZ.js +0 -1
- package/dist/m/c-WWXIDH4O.js +0 -28
- package/dist/m/c-XCNTK76U.js +0 -1
- package/dist/m/c-XMS6DPBR.js +0 -1
- package/dist/m/c-ZUCOKOHT.js +0 -1
- package/dist/m/c-ZVPVXTJF.js +0 -4
- package/dist/m/c-ZVRKXKM7.js +0 -1
- package/dist/m/core/autosave-HRYGUWV6.js +0 -1
- package/dist/m/core/conflict-notice-6MROJN2L.js +0 -1
- package/dist/m/core/is-edit-mode-KTWA3O5N.js +0 -1
- package/dist/m/core/recovery-FLTCC2OC.js +0 -1
- package/dist/m/core/save-HQZ4S667.js +0 -1
- package/dist/m/core/save-core-KGQWFUNH.js +0 -1
- package/dist/m/core/snapshot-EGQZNT45.js +0 -1
- package/dist/m/core/unsaved-warning-IDJLD66H.js +0 -1
- package/dist/m/loader-S4I7GKXR.js +0 -1
- package/dist/m/manifest-4C419D67.js +0 -1
- package/dist/m/manifest-edit-B077F288.js +0 -1
- package/dist/m/plugins/ai-edit-QN4PMMNL.js +0 -6
- package/dist/m/plugins/indicator-URDBBOQO.js +0 -1
- package/dist/m/plugins/live-HFWD4OIH.js +0 -1
- package/dist/m/plugins/menu-3HIEQSI6.js +0 -138
- package/dist/m/plugins/page-JOKB6XPR.js +0 -2
- package/dist/m/plugins/page-view-NXOJHNW6.js +0 -1
- package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
- package/dist/m/plugins/sortable-GWT4PWDV.js +0 -1
- package/dist/m/plugins/source-4ZRSAIYM.js +0 -1
- package/dist/m/plugins/undo-VDA5P7FH.js +0 -1
- package/dist/m/plugins/upgrade-W7FG5VFG.js +0 -1
- package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +0 -21
- package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +0 -1
- package/dist/m/reader-core-DXYCURFF.js +0 -68
- package/dist/m/route/route-KOCZSEGN.js +0 -1
- package/dist/m/sync/live-sync-J7VMYTKS.js +0 -1
- package/dist/m/ui/index-3JLVI27O.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-NRQDSWX6.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
|
|
@@ -237,10 +238,16 @@ export function onSnapshot(callback) {
|
|
|
237
238
|
* strings are only ever compared against each other, so they gain nothing from
|
|
238
239
|
* source fidelity and would pay a full render on every keystroke's dirty check.
|
|
239
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
|
+
*
|
|
240
244
|
* @param {?Function} renderer - (clone, today) => string, or null to restore outerHTML
|
|
245
|
+
* @param {Object} [options]
|
|
246
|
+
* @param {?Function} [options.steps] - (clone, today) => generator
|
|
241
247
|
*/
|
|
242
|
-
export function setSaveRenderer(renderer) {
|
|
248
|
+
export function setSaveRenderer(renderer, { steps = null } = {}) {
|
|
243
249
|
saveRenderer = renderer;
|
|
250
|
+
saveRendererSteps = steps;
|
|
244
251
|
}
|
|
245
252
|
|
|
246
253
|
const XHTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
@@ -255,7 +262,22 @@ const XHTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
|
255
262
|
* reads as literal text, and every later save escaped it again. The text is swapped for
|
|
256
263
|
* a marker only while serializing, so the clone holds the same nodes when this returns.
|
|
257
264
|
*/
|
|
258
|
-
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) {
|
|
259
281
|
const blocks = [];
|
|
260
282
|
const collect = (node) => {
|
|
261
283
|
for (const child of node.childNodes) {
|
|
@@ -269,21 +291,34 @@ function serializeClone(clone) {
|
|
|
269
291
|
}
|
|
270
292
|
};
|
|
271
293
|
collect(clone);
|
|
272
|
-
|
|
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) {
|
|
273
299
|
const nonce = Math.random().toString(36).slice(2);
|
|
274
|
-
|
|
300
|
+
return blocks.map(([el, kids], i) => {
|
|
275
301
|
const marker = `\uE000clay-noscript-${nonce}-${i}\uE000`;
|
|
276
302
|
el.replaceChildren(el.ownerDocument.createTextNode(marker));
|
|
277
303
|
return [marker, kids.map((n) => n.data).join('')];
|
|
278
304
|
});
|
|
279
|
-
|
|
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;
|
|
280
317
|
try {
|
|
281
|
-
|
|
318
|
+
return resume();
|
|
282
319
|
} finally {
|
|
283
|
-
|
|
320
|
+
activeCapture = previous;
|
|
284
321
|
}
|
|
285
|
-
for (const [marker, text] of raw) html = html.split(marker).join(text);
|
|
286
|
-
return html;
|
|
287
322
|
}
|
|
288
323
|
|
|
289
324
|
/**
|
|
@@ -303,15 +338,11 @@ function serializeSaveClone(clone, context = null) {
|
|
|
303
338
|
}
|
|
304
339
|
const today = serializeClone(clone);
|
|
305
340
|
if (!saveRenderer) return today;
|
|
306
|
-
const previous = activeCapture;
|
|
307
|
-
activeCapture = context;
|
|
308
341
|
try {
|
|
309
|
-
return saveRenderer(clone, today);
|
|
342
|
+
return runSaveRenderer(context, () => saveRenderer(clone, today));
|
|
310
343
|
} catch (err) {
|
|
311
344
|
console.error('clayjs: save renderer failed, sending the full serialization', err);
|
|
312
345
|
return today;
|
|
313
|
-
} finally {
|
|
314
|
-
activeCapture = previous;
|
|
315
346
|
}
|
|
316
347
|
}
|
|
317
348
|
|
|
@@ -343,13 +374,26 @@ export function addDocumentTransform(callback) {
|
|
|
343
374
|
*
|
|
344
375
|
* @returns {HTMLElement} Cloned document element with snapshot hooks applied
|
|
345
376
|
*/
|
|
346
|
-
function clonePreventingOnclone(node, deep = true) {
|
|
377
|
+
export function clonePreventingOnclone(node, deep = true) {
|
|
347
378
|
const prev = window.__preventOnclone;
|
|
348
379
|
window.__preventOnclone = true;
|
|
349
380
|
try { return node.cloneNode(deep); }
|
|
350
381
|
finally { window.__preventOnclone = prev; }
|
|
351
382
|
}
|
|
352
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
|
+
|
|
353
397
|
export function captureSnapshot({ flushUndo = true, authored = true, forSave = false, requestRefresh = false, context = null } = {}) {
|
|
354
398
|
// Force-close any pending undo idle batch BEFORE cloning the DOM, so the
|
|
355
399
|
// snapshot reflects a clean undo boundary. Without this, a save that fires
|
|
@@ -473,12 +517,16 @@ export function captureSaveClone({ flushUndo = true, authored = true } = {}) {
|
|
|
473
517
|
// check must not read that as an edit the person would lose on close.
|
|
474
518
|
const EMPTY_INERT_ATTRS = ['style', 'class'];
|
|
475
519
|
|
|
476
|
-
function
|
|
520
|
+
export function prepareForCompare(clone) {
|
|
477
521
|
stripDerived(clone);
|
|
478
522
|
for (const name of EMPTY_INERT_ATTRS) {
|
|
479
523
|
if (clone.getAttribute(name) === '') clone.removeAttribute(name);
|
|
480
524
|
for (const el of clone.querySelectorAll(`[${name}=""]`)) el.removeAttribute(name);
|
|
481
525
|
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export function serializeForCompare(clone) {
|
|
529
|
+
prepareForCompare(clone);
|
|
482
530
|
return "<!DOCTYPE html>" + clone.outerHTML;
|
|
483
531
|
}
|
|
484
532
|
|
|
@@ -565,6 +613,7 @@ export function captureForComparisonAndDirty({ flushUndo = true } = {}) {
|
|
|
565
613
|
const dirtyClone = clone.querySelector(NO_TRIGGER_AUTOSAVE_SELECTOR)
|
|
566
614
|
? clonePreventingOnclone(clone)
|
|
567
615
|
: null;
|
|
616
|
+
if (dirtyClone) carryProvenance(clone, dirtyClone);
|
|
568
617
|
|
|
569
618
|
for (const el of clone.querySelectorAll(STRIP_FROM_COMPARISON)) {
|
|
570
619
|
el.remove();
|
|
@@ -593,23 +642,32 @@ export function captureForComparisonAndDirty({ flushUndo = true } = {}) {
|
|
|
593
642
|
}
|
|
594
643
|
|
|
595
644
|
/**
|
|
596
|
-
*
|
|
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).
|
|
597
648
|
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
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.
|
|
600
653
|
*
|
|
601
654
|
* @param {Object} options
|
|
602
655
|
* @param {boolean} options.emitForSync - Whether to emit snapshot-ready event (default: true)
|
|
603
656
|
* @param {boolean} options.forRequest - These bytes go to a host, so the renderer may
|
|
604
657
|
* attach the verified model they came from, for the accepted save to adopt
|
|
605
|
-
* @
|
|
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
|
|
606
664
|
*/
|
|
607
|
-
export function
|
|
665
|
+
export function prepareSaveCapture({ emitForSync = true, flushUndo = true, requestRefresh = false, forRequest = false, pageFormatMarker = null, sliced = false } = {}) {
|
|
608
666
|
if (!Number.isSafeInteger(captureCounter + 1)) throw new Error('Save capture identity exhausted');
|
|
609
667
|
const captureId = ++captureCounter;
|
|
610
668
|
// The context this capture's renderer may attach a private artifact to, and the
|
|
611
669
|
// record of whether the page has its own save handlers. It never reaches the object
|
|
612
|
-
//
|
|
670
|
+
// the capture returns: that keeps its four public fields.
|
|
613
671
|
//
|
|
614
672
|
// Only for a capture whose bytes go to a host. A capture taken for anything else (a
|
|
615
673
|
// live-sync disk base, a download, an inspection) has no request to adopt it, so it
|
|
@@ -619,14 +677,12 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
619
677
|
|
|
620
678
|
// Emit for live-sync before any stripping.
|
|
621
679
|
//
|
|
622
|
-
// A listener gets the very clone this
|
|
623
|
-
//
|
|
624
|
-
//
|
|
625
|
-
// to it: anything it changes lands in the saved bytes and in both baselines,
|
|
626
|
-
// 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.
|
|
627
683
|
if (emitForSync) {
|
|
628
684
|
document.dispatchEvent(new CustomEvent('clay:snapshot-ready', {
|
|
629
|
-
detail: { documentElement: clone, forSave: true, captureId }
|
|
685
|
+
detail: { documentElement: clone, forSave: true, captureId, sliced }
|
|
630
686
|
}));
|
|
631
687
|
}
|
|
632
688
|
|
|
@@ -635,15 +691,14 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
635
691
|
|
|
636
692
|
// Clone for comparison before stripping (cheaper than cloning live DOM)
|
|
637
693
|
const compareClone = clonePreventingOnclone(clone);
|
|
694
|
+
carryProvenance(clone, compareClone);
|
|
638
695
|
|
|
639
|
-
// The dirty domain differs from the autosave domain only inside
|
|
640
|
-
//
|
|
641
|
-
// strip sets remove exactly the same nodes and one string serves both. The
|
|
642
|
-
// predicate reads the captured clone after authored handlers have run, and
|
|
643
|
-
// 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.
|
|
644
698
|
const dirtyClone = clone.querySelector(NO_TRIGGER_AUTOSAVE_SELECTOR)
|
|
645
699
|
? clonePreventingOnclone(clone)
|
|
646
700
|
: null;
|
|
701
|
+
if (dirtyClone) carryProvenance(clone, dirtyClone);
|
|
647
702
|
|
|
648
703
|
// Save clone: run hooks (freeze restore lives here), THEN strip [no-save]/[save-remove]
|
|
649
704
|
// LAST (snapshot-algorithm step 7) so freeze-restored [no-save] content can't leak to disk.
|
|
@@ -656,7 +711,6 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
656
711
|
el.remove();
|
|
657
712
|
}
|
|
658
713
|
stripDiagnosticBeacons(clone);
|
|
659
|
-
const forSave = serializeSaveClone(clone, context);
|
|
660
714
|
|
|
661
715
|
// Compare clone: strip every autosave-off region, then run hooks
|
|
662
716
|
for (const el of compareClone.querySelectorAll(STRIP_FROM_COMPARISON)) {
|
|
@@ -667,10 +721,8 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
667
721
|
hook(compareClone);
|
|
668
722
|
}
|
|
669
723
|
stripSnapshotRegions(compareClone);
|
|
670
|
-
const forComparison = serializeForCompare(compareClone);
|
|
671
724
|
|
|
672
725
|
// Dirty clone: same shape as the compare clone, one selector weaker.
|
|
673
|
-
let forDirty = forComparison;
|
|
674
726
|
if (dirtyClone) {
|
|
675
727
|
for (const el of dirtyClone.querySelectorAll(STRIP_FROM_DIRTY_CHECK)) {
|
|
676
728
|
el.remove();
|
|
@@ -680,14 +732,88 @@ export function captureForSaveAndComparison({ emitForSync = true, flushUndo = tr
|
|
|
680
732
|
hook(dirtyClone);
|
|
681
733
|
}
|
|
682
734
|
stripSnapshotRegions(dirtyClone);
|
|
683
|
-
forDirty = serializeForCompare(dirtyClone);
|
|
684
735
|
}
|
|
685
736
|
|
|
686
|
-
|
|
687
|
-
|
|
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;
|
|
688
774
|
return result;
|
|
689
775
|
}
|
|
690
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
|
+
|
|
691
817
|
/**
|
|
692
818
|
* Capture for a protected live-sync merge: the save-domain and loss-domain
|
|
693
819
|
* clones of ONE snapshot, plus a WeakMap pairing every loss-domain element to
|
|
@@ -822,6 +948,11 @@ export function serializeForSync(clone) {
|
|
|
822
948
|
return "<!DOCTYPE html>" + shell.slice(0, shell.length - endTag.length) + clone.innerHTML + endTag;
|
|
823
949
|
}
|
|
824
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
|
+
|
|
825
956
|
/**
|
|
826
957
|
* PHASE 1-2 (body only): For live-sync between admin users.
|
|
827
958
|
*
|