@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
package/src/source/session.js
CHANGED
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
*
|
|
29
29
|
* NOTHING HERE BLOCKS BOOT. The install is async and the loader does not wait for
|
|
30
30
|
* it. Only the save that asked for it waits, and a save after a failed install is the
|
|
31
|
-
* ordinary full serialization rather than an error.
|
|
31
|
+
* ordinary full serialization rather than an error. For a large document that wait is
|
|
32
|
+
* itself sliced: the model is parsed across tasks, and so is a queued repair, on the same
|
|
33
|
+
* driver and the same threshold the save's own preparation uses.
|
|
32
34
|
*
|
|
33
35
|
* ON DEMAND
|
|
34
36
|
*
|
|
@@ -50,15 +52,18 @@
|
|
|
50
52
|
|
|
51
53
|
import {
|
|
52
54
|
model,
|
|
55
|
+
modelSteps,
|
|
53
56
|
checkSource,
|
|
54
57
|
locate,
|
|
55
58
|
nodeAt,
|
|
56
59
|
pair,
|
|
57
|
-
|
|
60
|
+
pairCapturedSteps,
|
|
61
|
+
pairSteps,
|
|
58
62
|
render,
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
verifyForSaveSteps,
|
|
64
|
+
verifyRenderedSteps
|
|
61
65
|
} from '../core/source-map.js';
|
|
66
|
+
import { drain, runSliced } from '../lib/slice.js';
|
|
62
67
|
import {
|
|
63
68
|
attachCaptureArtifact,
|
|
64
69
|
captureSaveClone,
|
|
@@ -66,7 +71,8 @@ import {
|
|
|
66
71
|
originalSnapshotNode,
|
|
67
72
|
setSaveRenderer
|
|
68
73
|
} from '../core/snapshot.js';
|
|
69
|
-
import { onSaveAccepted } from '../core/save-core.js';
|
|
74
|
+
import { keepaliveHeld, onSaveAccepted } from '../core/save-core.js';
|
|
75
|
+
import { SLICED_SAVE_MIN_BYTES } from '../core/save-slices.js';
|
|
70
76
|
import { lastSeenEtag } from '../core/etag.js';
|
|
71
77
|
|
|
72
78
|
const state = {
|
|
@@ -110,6 +116,28 @@ const SUPERSEDED_RETRIES = 2;
|
|
|
110
116
|
|
|
111
117
|
const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
112
118
|
|
|
119
|
+
// The main-thread time one slice of an install or a repair may spend before handing the
|
|
120
|
+
// page back. The same budget the save's own preparation takes.
|
|
121
|
+
const SLICED_STEP_BUDGET_MS = 4;
|
|
122
|
+
|
|
123
|
+
// What an install's steps answer when a save or a disk frame replaced the file while they
|
|
124
|
+
// ran: no answer at all, and the caller asks again.
|
|
125
|
+
const SUPERSEDED = Object.freeze({ superseded: true });
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Whether a walk over bytes this big runs across tasks instead of in one.
|
|
129
|
+
*
|
|
130
|
+
* The threshold and both exceptions are the save's (core/save-slices.js): below
|
|
131
|
+
* `SLICED_SAVE_MIN_BYTES` the one-task walk is cheaper than the scheduling, a hidden tab
|
|
132
|
+
* has no typing to protect and its timers are throttled, and a keepalive save belongs to
|
|
133
|
+
* a page that is going away and cannot wait for a task boundary.
|
|
134
|
+
*/
|
|
135
|
+
function slicesWalk(bytes) {
|
|
136
|
+
if (document.hidden) return false;
|
|
137
|
+
if (keepaliveHeld()) return false;
|
|
138
|
+
return bytes >= SLICED_SAVE_MIN_BYTES;
|
|
139
|
+
}
|
|
140
|
+
|
|
113
141
|
const PROJECTED_MAP_PREFIX = '<script type="application/json" id="clay-runtime-map"';
|
|
114
142
|
|
|
115
143
|
/**
|
|
@@ -171,6 +199,11 @@ function pairAgainstPage(m) {
|
|
|
171
199
|
return pair(clone, m, originalSnapshotNode);
|
|
172
200
|
}
|
|
173
201
|
|
|
202
|
+
function* pairAgainstPageSteps(m) {
|
|
203
|
+
const clone = captureSaveClone({ flushUndo: false, authored: false });
|
|
204
|
+
return yield* pairSteps(clone, m, originalSnapshotNode);
|
|
205
|
+
}
|
|
206
|
+
|
|
174
207
|
/**
|
|
175
208
|
* The version a set of bytes names for itself, or null when nothing stamped it.
|
|
176
209
|
*
|
|
@@ -212,7 +245,7 @@ function staleVersion(m, context) {
|
|
|
212
245
|
return `source refused: the fetch answered version ${stamp} while this page holds ${held}`;
|
|
213
246
|
}
|
|
214
247
|
|
|
215
|
-
function install(sourceUrl, context = null, attempt = 0, render = true, latest = null) {
|
|
248
|
+
async function install(sourceUrl, context = null, attempt = 0, render = true, latest = null) {
|
|
216
249
|
const session = state.session;
|
|
217
250
|
const t0 = now();
|
|
218
251
|
const generation = state.adoptions;
|
|
@@ -225,40 +258,75 @@ function install(sourceUrl, context = null, attempt = 0, render = true, latest =
|
|
|
225
258
|
const receiptBytes = receipt && typeof receipt.bytes === 'string' && !isProjected(receipt.bytes)
|
|
226
259
|
? receipt.bytes
|
|
227
260
|
: null;
|
|
228
|
-
|
|
261
|
+
try {
|
|
262
|
+
const src = receiptBytes !== null ? receiptBytes : await fetchSource(sourceUrl);
|
|
229
263
|
if (session !== state.session) return null;
|
|
230
264
|
const t1 = now();
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
if (refused) throw new Error(`source refused: ${refused}`);
|
|
234
|
-
const stale = staleVersion(m, context);
|
|
235
|
-
if (stale) throw new Error(stale);
|
|
265
|
+
const answer = await runInstallSteps({ src, context, render, session, generation, t0, t1 });
|
|
266
|
+
if (answer !== SUPERSEDED) return answer;
|
|
236
267
|
// A save was accepted, or a disk frame carried newer bytes, while this answer was on
|
|
237
268
|
// its way. Those bytes are the file now, so ask again rather than adopt an older
|
|
238
269
|
// answer; a page that will not hold still is refused instead of retried forever.
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
const t2 = now();
|
|
244
|
-
const { map, stats } = pairAgainstPage(m);
|
|
245
|
-
const t3 = now();
|
|
246
|
-
|
|
247
|
-
state.model = m;
|
|
248
|
-
state.map = map;
|
|
249
|
-
state.stats = stats;
|
|
250
|
-
state.timing = { fetch: t1 - t0, model: t2 - t1, pair: t3 - t2, refresh: null };
|
|
251
|
-
state.installed = true;
|
|
252
|
-
if (render) setSaveRenderer(renderSave);
|
|
253
|
-
return summary();
|
|
254
|
-
}).catch((err) => {
|
|
270
|
+
if (attempt < SUPERSEDED_RETRIES) return install(sourceUrl, context, attempt + 1, render, latest);
|
|
271
|
+
throw new Error('source refused: a save replaced the file while the fetch was in flight');
|
|
272
|
+
} catch (err) {
|
|
255
273
|
// Not an error the page can do anything about, and not a failure of the save:
|
|
256
274
|
// saves go out exactly as they would without this plugin. Said once.
|
|
257
275
|
if (session !== state.session) return null;
|
|
258
276
|
state.refused = String(err && err.message ? err.message : err);
|
|
259
277
|
console.info(`clayjs: source map off (${state.refused}); saves use the full serialization`);
|
|
260
278
|
return null;
|
|
261
|
-
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Model and pair the bytes an install fetched, as steps.
|
|
284
|
+
*
|
|
285
|
+
* `drain` is the install this module always had and `runSliced` is the same walk across
|
|
286
|
+
* tasks, so a large document's first use hands the page back while its model is parsed
|
|
287
|
+
* and again while the page is paired against it: the pairing has a step form too, and
|
|
288
|
+
* yields every `YIELD_EVERY` nodes hashed or bound.
|
|
289
|
+
*
|
|
290
|
+
* Nothing is written to the session until the last yield is behind it, and an install
|
|
291
|
+
* whose page moved underneath it answers `SUPERSEDED` or null rather than installing a
|
|
292
|
+
* model of bytes the file no longer holds.
|
|
293
|
+
*/
|
|
294
|
+
function* installSteps(job) {
|
|
295
|
+
const m = yield* modelSteps(job.src);
|
|
296
|
+
const refused = checkSource(m, document);
|
|
297
|
+
if (refused) throw new Error(`source refused: ${refused}`);
|
|
298
|
+
const stale = staleVersion(m, job.context);
|
|
299
|
+
if (stale) throw new Error(stale);
|
|
300
|
+
if (state.adoptions !== job.generation) return SUPERSEDED;
|
|
301
|
+
const t2 = now();
|
|
302
|
+
yield;
|
|
303
|
+
if (state.session !== job.session) return null;
|
|
304
|
+
if (state.adoptions !== job.generation) return SUPERSEDED;
|
|
305
|
+
const { map, stats } = yield* pairAgainstPageSteps(m);
|
|
306
|
+
if (state.session !== job.session) return null;
|
|
307
|
+
if (state.adoptions !== job.generation) return SUPERSEDED;
|
|
308
|
+
const t3 = now();
|
|
309
|
+
|
|
310
|
+
state.model = m;
|
|
311
|
+
state.map = map;
|
|
312
|
+
state.stats = stats;
|
|
313
|
+
state.timing = { fetch: job.t1 - job.t0, model: t2 - job.t1, pair: t3 - t2, refresh: null };
|
|
314
|
+
state.installed = true;
|
|
315
|
+
if (job.render) setSaveRenderer(renderSave, { steps: renderSaveSteps });
|
|
316
|
+
return summary();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Run one install's steps in this task or across tasks, by the document's size.
|
|
321
|
+
*
|
|
322
|
+
* A session that ends while the walk yields aborts it, so a late answer installs nothing,
|
|
323
|
+
* which is the rule the fetch half already followed.
|
|
324
|
+
*/
|
|
325
|
+
async function runInstallSteps(job) {
|
|
326
|
+
if (!slicesWalk(job.src.length)) return drain(installSteps(job));
|
|
327
|
+
const signal = { get aborted() { return state.session !== job.session; } };
|
|
328
|
+
const answer = await runSliced(installSteps(job), { budgetMs: SLICED_STEP_BUDGET_MS, signal });
|
|
329
|
+
return answer && answer.aborted === true ? null : answer;
|
|
262
330
|
}
|
|
263
331
|
|
|
264
332
|
/**
|
|
@@ -292,12 +360,28 @@ function install(sourceUrl, context = null, attempt = 0, render = true, latest =
|
|
|
292
360
|
* describes the live page there.
|
|
293
361
|
*/
|
|
294
362
|
export function renderSave(clone, today, opts = {}) {
|
|
363
|
+
return drain(renderSaveSteps(clone, today, opts));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* The same render, sliced: exactly one generator, so a save can run its verification
|
|
368
|
+
* across tasks without a second copy of this logic. Every assignment to the session
|
|
369
|
+
* happens after the last yield of its phase, so a slice that is aborted leaves the
|
|
370
|
+
* session as it was found and the next save renders from the same state.
|
|
371
|
+
*
|
|
372
|
+
* The generation the render started from is recorded, because a morph or an accepted
|
|
373
|
+
* save can re-pair the map while the verifier is yielding. A render whose bytes were
|
|
374
|
+
* laid out against a map that has since been rebuilt describes a page that is no longer
|
|
375
|
+
* here, so it is refused like any other render that cannot be trusted: today's bytes.
|
|
376
|
+
*/
|
|
377
|
+
export function* renderSaveSteps(clone, today, opts = {}) {
|
|
295
378
|
if (!state.installed) return today;
|
|
296
379
|
// A refresh still waiting for idle time runs now, so this save never renders from a
|
|
297
380
|
// map a morph made stale. It pairs against the page, not this clone: onbeforesave may
|
|
298
381
|
// have changed the clone, and the map must describe the live page.
|
|
299
|
-
|
|
300
|
-
|
|
382
|
+
yield* refreshNowSteps();
|
|
383
|
+
const session = state.session;
|
|
384
|
+
const generation = state.repairs;
|
|
301
385
|
const capture = currentCaptureContext();
|
|
302
386
|
const adoptable = !!capture && !capture.authoredHandlers;
|
|
303
387
|
const print = new Set();
|
|
@@ -312,15 +396,21 @@ export function renderSave(clone, today, opts = {}) {
|
|
|
312
396
|
// The verifier's retained parse is a cost, so it is only asked for when there is a
|
|
313
397
|
// capture that could adopt it.
|
|
314
398
|
const v = adoptable
|
|
315
|
-
?
|
|
316
|
-
:
|
|
399
|
+
? yield* verifyForSaveSteps(out.text, today, document, clone, state.model.parseErrors)
|
|
400
|
+
: yield* verifyRenderedSteps(out.text, today, document, clone, state.model.parseErrors, false);
|
|
317
401
|
if (v.ok) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
402
|
+
const stale = renderSuperseded(session, generation);
|
|
403
|
+
if (stale) return fallback(stale, today);
|
|
404
|
+
const outcome = print.size
|
|
321
405
|
? { text: out.text, scope: 'partial', reason: firstDiff, printed: print.size }
|
|
322
406
|
: { text: out.text, scope: null };
|
|
323
|
-
|
|
407
|
+
const paired = v.model ? yield* pairCapturedSteps(clone, v.model, originalSnapshotNode) : null;
|
|
408
|
+
const late = renderSuperseded(session, generation);
|
|
409
|
+
if (late) return fallback(late, today);
|
|
410
|
+
state.lastRenderMs = out.ms;
|
|
411
|
+
state.lastBytes = out.text.length;
|
|
412
|
+
state.lastOutcome = outcome;
|
|
413
|
+
if (v.model) offerCandidate(v.model, out.text, capture, paired);
|
|
324
414
|
return out.text;
|
|
325
415
|
}
|
|
326
416
|
if (firstDiff === null) firstDiff = v.diff;
|
|
@@ -329,6 +419,17 @@ export function renderSave(clone, today, opts = {}) {
|
|
|
329
419
|
return fallback(firstDiff, today);
|
|
330
420
|
}
|
|
331
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Why a render this generator laid out can no longer be trusted, or null. Only a yield
|
|
424
|
+
* can make this answer yes: a drained render never leaves this task.
|
|
425
|
+
*/
|
|
426
|
+
function renderSuperseded(session, generation) {
|
|
427
|
+
if (state.session !== session) return 'render refused: the session ended while this render ran';
|
|
428
|
+
if (!state.installed) return 'render refused: the source map was uninstalled while this render ran';
|
|
429
|
+
if (state.repairs !== generation) return 'render refused: the map was re-paired while this render ran';
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
|
|
332
433
|
/**
|
|
333
434
|
* Hand this capture the model of the bytes it just verified, or nothing.
|
|
334
435
|
*
|
|
@@ -337,8 +438,7 @@ export function renderSave(clone, today, opts = {}) {
|
|
|
337
438
|
* does not correspond, node for node, leaves the capture without an artifact, and the
|
|
338
439
|
* accepted save takes the inspection path it takes today.
|
|
339
440
|
*/
|
|
340
|
-
function offerCandidate(m, html,
|
|
341
|
-
const paired = pairCaptured(clone, m, originalSnapshotNode);
|
|
441
|
+
function offerCandidate(m, html, capture, paired) {
|
|
342
442
|
if (!paired) return;
|
|
343
443
|
attachCaptureArtifact({
|
|
344
444
|
captureId: capture.captureId,
|
|
@@ -506,25 +606,76 @@ function queueRepair(event) {
|
|
|
506
606
|
function scheduleRefresh() {
|
|
507
607
|
if (state.refreshQueued) return;
|
|
508
608
|
state.refreshQueued = true;
|
|
509
|
-
if (typeof requestIdleCallback === 'function') requestIdleCallback(
|
|
510
|
-
else setTimeout(
|
|
609
|
+
if (typeof requestIdleCallback === 'function') requestIdleCallback(refreshSoon, { timeout: 2000 });
|
|
610
|
+
else setTimeout(refreshSoon, 0);
|
|
511
611
|
}
|
|
512
612
|
|
|
513
613
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
614
|
+
* The queued refresh, run the way this document's size allows: in the caller's task for a
|
|
615
|
+
* small one, across tasks for a large one, so a repair nobody is waiting for does not hold
|
|
616
|
+
* the page for its whole duration.
|
|
617
|
+
*/
|
|
618
|
+
function refreshSoon() {
|
|
619
|
+
if (!slicesWalk(refreshBytes())) { refreshNow(); return; }
|
|
620
|
+
runSlicedRefresh();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* How big the bytes a queued refresh is about to model are: the accepted save's or a disk
|
|
625
|
+
* frame's when there are any, else the model this session already holds.
|
|
626
|
+
*/
|
|
627
|
+
function refreshBytes() {
|
|
628
|
+
if (typeof state.pendingBytes === 'string') return state.pendingBytes.length;
|
|
629
|
+
return state.model ? state.model.src.length : 0;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* A refresh across tasks, and the one thing a sliced walk needs that a drained one does
|
|
634
|
+
* not: a walk that stopped because something newer arrived left the queue in place, and
|
|
635
|
+
* that newer answer's `scheduleRefresh` had nothing to do because the flag was still set.
|
|
636
|
+
* So the same work is asked for again here. A session that ended drops it instead.
|
|
637
|
+
*/
|
|
638
|
+
async function runSlicedRefresh() {
|
|
639
|
+
const session = state.session;
|
|
640
|
+
const signal = { get aborted() { return state.session !== session; } };
|
|
641
|
+
try {
|
|
642
|
+
await runSliced(refreshNowSteps(), { budgetMs: SLICED_STEP_BUDGET_MS, signal });
|
|
643
|
+
} catch (err) {
|
|
644
|
+
console.warn('clayjs: source map could not run the queued repair:', err);
|
|
645
|
+
}
|
|
646
|
+
if (!state.refreshQueued || !state.installed || state.session !== session) return;
|
|
647
|
+
state.refreshQueued = false;
|
|
648
|
+
scheduleRefresh();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Run a queued refresh now, in this task. A no-op when none is queued, which is also what
|
|
653
|
+
* makes the idle callback of a refresh that `text()` or `locate()` already ran harmless.
|
|
654
|
+
* Everything that reads the session synchronously comes through here: it must answer in
|
|
655
|
+
* the caller's task, so it drains the same steps the idle refresh may resume across tasks.
|
|
516
656
|
*/
|
|
517
657
|
function refreshNow() {
|
|
658
|
+
drain(refreshNowSteps());
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* The same refresh, sliced: modelling yields, and nothing the session holds is written
|
|
663
|
+
* until the last yield is behind it.
|
|
664
|
+
*
|
|
665
|
+
* An aborted slice must leave the queued refresh queued, with its bytes, so the work is
|
|
666
|
+
* simply done again — nothing is half-adopted, and no counter is charged twice. That is
|
|
667
|
+
* why the queue is read into locals and committed at the end, and why a newer model,
|
|
668
|
+
* adoption or session arriving during a yield abandons this attempt rather than
|
|
669
|
+
* overwriting it: the newer one left its own refresh queued.
|
|
670
|
+
*/
|
|
671
|
+
export function* refreshNowSteps() {
|
|
518
672
|
if (!state.refreshQueued) return;
|
|
519
|
-
state.refreshQueued = false;
|
|
520
|
-
|
|
673
|
+
if (!state.installed) { state.refreshQueued = false; return; }
|
|
674
|
+
const session = state.session;
|
|
675
|
+
const repairs = state.repairs;
|
|
676
|
+
const adoptions = state.adoptions;
|
|
521
677
|
const bytes = state.pendingBytes;
|
|
522
|
-
state.pendingBytes = null;
|
|
523
678
|
const refetch = state.refetch && bytes === null;
|
|
524
|
-
if (refetch) state.refetch = false;
|
|
525
|
-
// Counted even when the re-pair below fails: either way this page is not the page a
|
|
526
|
-
// candidate captured before it was built against.
|
|
527
|
-
state.repairs++;
|
|
528
679
|
const t = now();
|
|
529
680
|
// The two halves fail independently, so they are tried independently. A refused
|
|
530
681
|
// re-model used to skip the re-pair with it, and the re-pair is the half that
|
|
@@ -535,7 +686,7 @@ function refreshNow() {
|
|
|
535
686
|
let m = state.model;
|
|
536
687
|
if (bytes !== null) {
|
|
537
688
|
try {
|
|
538
|
-
const next =
|
|
689
|
+
const next = yield* modelSteps(bytes);
|
|
539
690
|
const refused = checkSource(next, document);
|
|
540
691
|
if (refused) throw new Error(refused);
|
|
541
692
|
m = next;
|
|
@@ -543,22 +694,39 @@ function refreshNow() {
|
|
|
543
694
|
console.warn('clayjs: source map kept the previous model, the accepted bytes did not model:', err);
|
|
544
695
|
}
|
|
545
696
|
}
|
|
697
|
+
yield;
|
|
698
|
+
if (!superseded(session, repairs, adoptions)) return;
|
|
699
|
+
let paired = null;
|
|
546
700
|
try {
|
|
547
|
-
|
|
548
|
-
state.model = m;
|
|
549
|
-
state.map = map;
|
|
550
|
-
state.stats = stats;
|
|
551
|
-
state.refreshes++;
|
|
552
|
-
state.timing.refresh = now() - t;
|
|
701
|
+
paired = pairAgainstPage(m);
|
|
553
702
|
} catch (err) {
|
|
554
703
|
// The map is now stale rather than wrong: it still describes the pairing as of
|
|
555
704
|
// the last successful refresh. Renders off a stale map verify or fall back like
|
|
556
705
|
// any other, so this costs formatting fidelity and nothing else.
|
|
557
706
|
console.warn('clayjs: source map could not re-pair, continuing on the previous map:', err);
|
|
558
707
|
}
|
|
708
|
+
state.refreshQueued = false;
|
|
709
|
+
state.pendingBytes = null;
|
|
710
|
+
if (refetch) state.refetch = false;
|
|
711
|
+
// Counted even when the re-pair above failed: either way this page is not the page a
|
|
712
|
+
// candidate captured before it was built against.
|
|
713
|
+
state.repairs++;
|
|
714
|
+
if (paired) {
|
|
715
|
+
state.model = m;
|
|
716
|
+
state.map = paired.map;
|
|
717
|
+
state.stats = paired.stats;
|
|
718
|
+
state.refreshes++;
|
|
719
|
+
state.timing.refresh = now() - t;
|
|
720
|
+
}
|
|
559
721
|
if (refetch) refetchModel();
|
|
560
722
|
}
|
|
561
723
|
|
|
724
|
+
/** Whether something newer than the refresh this generator started arrived while it yielded. */
|
|
725
|
+
function superseded(session, repairs, adoptions) {
|
|
726
|
+
return state.session === session && state.installed
|
|
727
|
+
&& state.repairs === repairs && state.adoptions === adoptions;
|
|
728
|
+
}
|
|
729
|
+
|
|
562
730
|
/**
|
|
563
731
|
* Take the model from the file itself, after a disk frame arrived with bytes this host
|
|
564
732
|
* projected rather than bytes on disk.
|
|
@@ -664,6 +832,14 @@ export function stats() {
|
|
|
664
832
|
|
|
665
833
|
export const installed = () => state.installed;
|
|
666
834
|
|
|
835
|
+
/**
|
|
836
|
+
* How many times the map has been rebuilt since this session installed.
|
|
837
|
+
*
|
|
838
|
+
* A render is refused when this changes while it runs, so a save that prepares across
|
|
839
|
+
* tasks falls back to today's bytes rather than a pairing that no longer describes the page.
|
|
840
|
+
*/
|
|
841
|
+
export const repairGeneration = () => state.repairs;
|
|
842
|
+
|
|
667
843
|
/** The facade's dispose: take the install back, and invalidate whatever was still loading. */
|
|
668
844
|
export function release() {
|
|
669
845
|
uninstall();
|
|
@@ -36,6 +36,18 @@ export class EditingSender {
|
|
|
36
36
|
for (const type of EVENTS) document.addEventListener(type, this._onEvent);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Speak again after a quiesce that was refused: the listeners come back and the
|
|
41
|
+
* target this tab holds goes again, because the clear that came with the quiesce
|
|
42
|
+
* took the sender's last word off the host.
|
|
43
|
+
*/
|
|
44
|
+
resume() {
|
|
45
|
+
if (!this.stopped) return;
|
|
46
|
+
this.start();
|
|
47
|
+
this.sent = null;
|
|
48
|
+
this.schedule();
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
stop() {
|
|
40
52
|
this.stopped = true;
|
|
41
53
|
for (const type of EVENTS) document.removeEventListener(type, this._onEvent);
|
|
@@ -26,12 +26,29 @@ export function syncParticipant(sync) {
|
|
|
26
26
|
// published cannot outlive the session that published it. A page target is
|
|
27
27
|
// released with the ownership it came with; the generic marker is cleared
|
|
28
28
|
// outright. Neither falls back to a positional marker: the exit publishes nobody.
|
|
29
|
+
// The sender is stopped rather than only cleared, so the gate stays shut until the
|
|
30
|
+
// exit commits or is refused: a reconnect, a roster change or a focus move during
|
|
31
|
+
// the barrier would otherwise publish a target the session never takes back. The
|
|
32
|
+
// page's own awareness hears the same barrier and resumes with it, so its caret
|
|
33
|
+
// goes quiet with the sender and comes back on a refused exit.
|
|
29
34
|
const quiesce = () => {
|
|
35
|
+
sync._presenceQuiesced = true;
|
|
36
|
+
sync._editing?.stop();
|
|
30
37
|
presence.setGeneric(null);
|
|
31
38
|
presence.releaseTarget();
|
|
39
|
+
if (typeof document !== "undefined") document.dispatchEvent(new Event("clay:presence-quiesce"));
|
|
32
40
|
return sync.lane === "live" && sync._morphInFlight ? { ok: false, code: "pending-input" } : { ok: true };
|
|
33
41
|
};
|
|
34
|
-
const
|
|
42
|
+
const resume = () => {
|
|
43
|
+
sync._presenceQuiesced = false;
|
|
44
|
+
sync._editing?.resume();
|
|
45
|
+
if (typeof document !== "undefined") document.dispatchEvent(new Event("clay:presence-resume"));
|
|
46
|
+
};
|
|
47
|
+
const dispose = () => {
|
|
48
|
+
sync._presenceQuiesced = false;
|
|
49
|
+
sync.stop();
|
|
50
|
+
if (typeof document !== "undefined") document.dispatchEvent(new Event("clay:presence-resume"));
|
|
51
|
+
};
|
|
35
52
|
return {
|
|
36
53
|
id: "clay:sync",
|
|
37
54
|
order: 50,
|
|
@@ -39,10 +56,11 @@ export function syncParticipant(sync) {
|
|
|
39
56
|
await loadConflictsEdit();
|
|
40
57
|
// Adoption: the explicit initial edit started this lane as the module
|
|
41
58
|
// evaluated, so the lease covers it and starts nothing new.
|
|
42
|
-
if (context?.initial) return { quiesce, dispose };
|
|
59
|
+
if (context?.initial) return { quiesce, resume, dispose };
|
|
43
60
|
let started = false;
|
|
44
61
|
return {
|
|
45
62
|
quiesce,
|
|
63
|
+
resume,
|
|
46
64
|
// The commit: the lane a reader was following stops, and the same stream
|
|
47
65
|
// slot opens on the live lane.
|
|
48
66
|
enable() {
|
|
@@ -55,7 +73,9 @@ export function syncParticipant(sync) {
|
|
|
55
73
|
// lane was the reader's: stopping it would take the saved lane the switch gives
|
|
56
74
|
// back with it.
|
|
57
75
|
dispose: () => {
|
|
76
|
+
sync._presenceQuiesced = false;
|
|
58
77
|
if (started) sync.stop();
|
|
78
|
+
if (typeof document !== "undefined") document.dispatchEvent(new Event("clay:presence-resume"));
|
|
59
79
|
},
|
|
60
80
|
};
|
|
61
81
|
},
|