@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.
Files changed (467) hide show
  1. package/dist/all.js +1 -1
  2. package/dist/clay-dom.js +1 -1
  3. package/dist/clay-events.js +1 -1
  4. package/dist/clay-internals.js +1 -1
  5. package/dist/clay-options.js +1 -1
  6. package/dist/clay-router.js +1 -1
  7. package/dist/clay-ui.js +1 -1
  8. package/dist/clay-utils.js +1 -1
  9. package/dist/clay.js +1 -1
  10. package/dist/clay.standalone.js +72474 -62090
  11. package/dist/m/attrs/{save-freeze-MYNVXREN.js → save-freeze-LWGF6XUN.js} +1 -1
  12. package/dist/m/c-2GRUM77Q.js +30 -0
  13. package/dist/m/c-2KIEGNC4.js +1 -0
  14. package/dist/m/{c-AMSNPOCX.js → c-2KL3BD52.js} +1 -1
  15. package/dist/m/c-2ZZBC45O.js +1 -0
  16. package/dist/m/c-372N6H3B.js +1 -0
  17. package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
  18. package/dist/m/c-3LDGIBMN.js +1 -0
  19. package/dist/m/c-3O7OBDCM.js +1 -0
  20. package/dist/m/c-4ASQPRQL.js +1 -0
  21. package/dist/m/c-4TXNLX4K.js +6 -0
  22. package/dist/m/c-4YU3BR4D.js +6 -0
  23. package/dist/m/c-5734MF2P.js +1 -0
  24. package/dist/m/c-57XDY6HC.js +1 -0
  25. package/dist/m/{c-KAU6E43D.js → c-5B4YBZXC.js} +1 -1
  26. package/dist/m/c-5ONTYIUI.js +1 -0
  27. package/dist/m/c-5OOVFCJM.js +1 -0
  28. package/dist/m/{c-3XIRD4NU.js → c-5T6NZHNM.js} +1 -1
  29. package/dist/m/{c-74MGYWZF.js → c-5ZQV7KC4.js} +1 -1
  30. package/dist/m/{c-7OLLJPWP.js → c-6DB2OLRJ.js} +1 -1
  31. package/dist/m/c-6DNRCPNM.js +1 -0
  32. package/dist/m/c-6UGWFMQC.js +2 -0
  33. package/dist/m/c-7K4SCQB7.js +1 -0
  34. package/dist/m/c-7MG5RU63.js +1 -0
  35. package/dist/m/c-7PRJCA3E.js +1 -0
  36. package/dist/m/c-A6L3FMUS.js +1 -0
  37. package/dist/m/c-AMU3BPPR.js +4 -0
  38. package/dist/m/{c-Y5HBBIQ4.js → c-AMYEFA2E.js} +1 -1
  39. package/dist/m/c-AQYCG7QW.js +1 -0
  40. package/dist/m/c-ATKAOH23.js +1 -0
  41. package/dist/m/c-B5CS55WR.js +1 -0
  42. package/dist/m/c-B5UJQT7W.js +1 -0
  43. package/dist/m/c-B7J4K2AE.js +3 -0
  44. package/dist/m/c-BHZYSHW4.js +1 -0
  45. package/dist/m/{c-VHM5XQHK.js → c-BIJALSRD.js} +1 -1
  46. package/dist/m/c-BJ7YQ5QZ.js +1 -0
  47. package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
  48. package/dist/m/{c-VE6ZMYMC.js → c-BUAYFEV5.js} +1 -1
  49. package/dist/m/c-C6IPRG34.js +1 -0
  50. package/dist/m/{c-KU3ZFADW.js → c-CAS3NND4.js} +1 -1
  51. package/dist/m/{c-PS5ARVWR.js → c-CCQ25GFY.js} +1 -1
  52. package/dist/m/c-CJFDLD43.js +1 -0
  53. package/dist/m/c-COPTL5WB.js +1 -0
  54. package/dist/m/c-D3E4WJ4M.js +166 -0
  55. package/dist/m/c-D5IKEKXW.js +7 -0
  56. package/dist/m/c-DHBRA6DL.js +1 -0
  57. package/dist/m/c-DKLS74XB.js +72 -0
  58. package/dist/m/c-DVYGFTSK.js +3 -0
  59. package/dist/m/c-E3ZXARWA.js +1 -0
  60. package/dist/m/c-EK63G3CG.js +1 -0
  61. package/dist/m/c-ERQSNR6K.js +13 -0
  62. package/dist/m/c-EVYAISP3.js +1 -0
  63. package/dist/m/c-EXKHPOVC.js +3 -0
  64. package/dist/m/{c-QJ4OM3G6.js → c-FWKHM4E7.js} +1 -1
  65. package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
  66. package/dist/m/c-GNDL3TKB.js +1 -0
  67. package/dist/m/c-GRWMD6O5.js +1 -0
  68. package/dist/m/c-GRXHHFPO.js +1 -0
  69. package/dist/m/c-HG2C77BM.js +4 -0
  70. package/dist/m/c-HHGJBU4D.js +1 -0
  71. package/dist/m/c-HMV6P4RX.js +1 -0
  72. package/dist/m/c-HMYWIHFG.js +1 -0
  73. package/dist/m/c-HT2DKBV2.js +1 -0
  74. package/dist/m/c-I2SCCUEW.js +1 -0
  75. package/dist/m/{c-TJ6WOJL3.js → c-ICJYHABF.js} +2 -2
  76. package/dist/m/c-IMZCWYHR.js +7 -0
  77. package/dist/m/c-IVMD3FEM.js +1 -0
  78. package/dist/m/{c-WDC5GVKU.js → c-IVTRVENZ.js} +1 -1
  79. package/dist/m/c-JKZJIUIP.js +1 -0
  80. package/dist/m/{c-UDUUJPFZ.js → c-JNLX3RJB.js} +1 -1
  81. package/dist/m/c-K3ADYNLU.js +1 -0
  82. package/dist/m/c-KGHVFRR3.js +1 -0
  83. package/dist/m/{c-5QWQ7A5L.js → c-KJJPV77R.js} +1 -1
  84. package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
  85. package/dist/m/{c-EZU56NQV.js → c-KP64LT2A.js} +2 -2
  86. package/dist/m/c-KQ7E3PYY.js +1 -0
  87. package/dist/m/c-L3F5ZLIV.js +1 -0
  88. package/dist/m/{c-3RUU4TPX.js → c-L42OLXWM.js} +3 -2
  89. package/dist/m/c-L5VFATT6.js +1 -0
  90. package/dist/m/c-L7JL5TL3.js +3 -0
  91. package/dist/m/c-LKSL4AUJ.js +1 -0
  92. package/dist/m/c-M6FNQCTQ.js +1 -0
  93. package/dist/m/c-MCOOHCSG.js +1 -0
  94. package/dist/m/c-MHI3GYKF.js +1 -0
  95. package/dist/m/c-MWXFLW4J.js +1 -0
  96. package/dist/m/{c-5RJFTLSF.js → c-NEL7HQW3.js} +1 -1
  97. package/dist/m/{c-DCXOXBF3.js → c-NQJRVJ56.js} +1 -1
  98. package/dist/m/c-NWX3MFT4.js +1 -0
  99. package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
  100. package/dist/m/c-OKXI52N7.js +1 -0
  101. package/dist/m/{c-R3L7ZJGB.js → c-OPLUZR3A.js} +1 -1
  102. package/dist/m/{c-GSFIGKOP.js → c-OTMKN2OO.js} +1 -1
  103. package/dist/m/{c-6NC5I2TV.js → c-OYRZ7XG2.js} +1 -1
  104. package/dist/m/{c-EVCQ3BKA.js → c-P3OFYUMA.js} +1 -1
  105. package/dist/m/c-PLN76GF3.js +6 -0
  106. package/dist/m/c-PLP4JKFC.js +1 -0
  107. package/dist/m/c-Q26MME6N.js +1 -0
  108. package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
  109. package/dist/m/{c-FRLHSY7E.js → c-QIKT6SKM.js} +1 -1
  110. package/dist/m/c-QIXT3Q7C.js +28 -0
  111. package/dist/m/c-QO4HNGCQ.js +1 -0
  112. package/dist/m/{c-JU3NK25H.js → c-R27DWQKX.js} +1 -1
  113. package/dist/m/{c-RSMOODDU.js → c-R2XUODVY.js} +1 -1
  114. package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
  115. package/dist/m/c-RND3G2S2.js +1 -0
  116. package/dist/m/{c-Q622NS2Y.js → c-S2U6NJU4.js} +1 -1
  117. package/dist/m/c-SPZPGD3R.js +1 -0
  118. package/dist/m/c-SW4YEG2D.js +8 -0
  119. package/dist/m/c-T637T7SN.js +1 -0
  120. package/dist/m/c-TJAXGIRV.js +1 -0
  121. package/dist/m/{c-RYUXUUBX.js → c-TNEM3RNM.js} +1 -1
  122. package/dist/m/c-TTB5EK4S.js +32 -0
  123. package/dist/m/c-U7SHNWDH.js +1 -0
  124. package/dist/m/c-UDHIVLYH.js +20 -0
  125. package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
  126. package/dist/m/{c-QICP2YR5.js → c-UORXLSD3.js} +1 -1
  127. package/dist/m/c-UPD33ZPE.js +1 -0
  128. package/dist/m/c-URSMSFCB.js +1 -0
  129. package/dist/m/c-UY4QX42B.js +1 -0
  130. package/dist/m/c-V44KWI2L.js +4 -0
  131. package/dist/m/c-VILENZTD.js +1 -0
  132. package/dist/m/{c-HDDEEL77.js → c-VJYEVRYK.js} +1 -1
  133. package/dist/m/{c-SMBZG5X4.js → c-VR2BJ4AN.js} +1 -1
  134. package/dist/m/c-W4JCVLAB.js +1 -0
  135. package/dist/m/c-W6DQSHXI.js +135 -0
  136. package/dist/m/c-WBGWGOM7.js +1 -0
  137. package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
  138. package/dist/m/c-WGW764WX.js +1 -0
  139. package/dist/m/c-WMRJKQ2V.js +5 -0
  140. package/dist/m/c-WNTYKCDG.js +1 -0
  141. package/dist/m/c-WQWIRWO3.js +72 -0
  142. package/dist/m/c-X5LNHET5.js +1 -0
  143. package/dist/m/{c-I2ZATQCD.js → c-X5TSXZYJ.js} +2 -2
  144. package/dist/m/c-XGH4GS2T.js +1 -0
  145. package/dist/m/c-Y6F2FORL.js +1 -0
  146. package/dist/m/c-YBKHUAK7.js +10 -0
  147. package/dist/m/c-YBSS55KU.js +20 -0
  148. package/dist/m/c-YCLU4UNT.js +2 -0
  149. package/dist/m/c-YVZ2L7CE.js +1 -0
  150. package/dist/m/c-ZK4GIZH2.js +1 -0
  151. package/dist/m/c-ZRXJD4G6.js +1 -0
  152. package/dist/m/c-ZWBI4HYK.js +1 -0
  153. package/dist/m/core/{admin-attrs-QHOKPJ7W.js → admin-attrs-YY7T5FGU.js} +1 -1
  154. package/dist/m/core/autosave-S6OMNDCK.js +1 -0
  155. package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
  156. package/dist/m/core/{host-permission-FNXVEFNE.js → host-permission-6EVJGNPL.js} +1 -1
  157. package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
  158. package/dist/m/core/{persist-JFYRHMRJ.js → persist-UKXV5KLA.js} +1 -1
  159. package/dist/m/core/recovery-DRFZRJED.js +1 -0
  160. package/dist/m/core/save-LYY7K6U7.js +1 -0
  161. package/dist/m/core/save-core-MKRPSEEO.js +1 -0
  162. package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
  163. package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
  164. package/dist/m/internals/{index-PVXGPUDA.js → index-TGLQINN3.js} +1 -1
  165. package/dist/m/loader-3BBNZZGM.js +1 -0
  166. package/dist/m/manifest-F449FF98.js +1 -0
  167. package/dist/m/manifest-edit-4396A009.js +1 -0
  168. package/dist/m/manifest.json +2111 -1573
  169. package/dist/m/page/{editor-NNJA4OKD.js → editor-6LRB3GE4.js} +123 -21
  170. package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
  171. package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
  172. package/dist/m/plugins/{demo-RM5IR6AO.js → demo-OYO5GCAO.js} +1 -1
  173. package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
  174. package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
  175. package/dist/m/plugins/live-AD4H6XSL.js +1 -0
  176. package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
  177. package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
  178. package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
  179. package/dist/m/plugins/{people-SGPJKANK.js → people-JCHVPF7P.js} +1 -1
  180. package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
  181. package/dist/m/plugins/{richclay-NRQDSWX6.js → richclay-KLGETK77.js} +2 -2
  182. package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
  183. package/dist/m/plugins/source-TJXOIOYY.js +1 -0
  184. package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
  185. package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
  186. package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
  187. package/dist/m/plugins/{upload-WLYKK6TL.js → upload-NHGJXZK2.js} +1 -1
  188. package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
  189. package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
  190. package/dist/m/plugins/{wire-4N6GYAF6.js → wire-VBWXEXU2.js} +1 -1
  191. package/dist/m/reader-core-4W2KYEWQ.js +77 -0
  192. package/dist/m/route/route-62CHXL5B.js +1 -0
  193. package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
  194. package/dist/m/ui/index-IVWN2QC4.js +1 -0
  195. package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
  196. package/dist/offline-manifest.json +1367 -930
  197. package/dist/page/themes/light-1.css +8 -7
  198. package/dist/server/core.cjs +1440 -288
  199. package/dist/server/offline-runtime.cjs +63 -63
  200. package/dist/server/offline-runtime.mjs +63 -63
  201. package/dist/server/page-compaction-worker.cjs +3710 -0
  202. package/dist/server/rooms.mjs +5 -0
  203. package/docs/guide.md +653 -101
  204. package/entries/sap.js +5 -5
  205. package/package.json +1 -1
  206. package/src/core/admin-contenteditable.js +5 -2
  207. package/src/core/admin-inputs.js +1 -0
  208. package/src/core/admin-onclick.js +2 -1
  209. package/src/core/conflict-notice-ui.js +4 -7
  210. package/src/core/download.js +2 -2
  211. package/src/core/host-meta.js +38 -0
  212. package/src/core/is-edit-mode.js +17 -1
  213. package/src/core/mode-key.js +125 -19
  214. package/src/core/mode-support.js +5 -0
  215. package/src/core/mode-transition.js +20 -6
  216. package/src/core/preserve-authored.js +4 -4
  217. package/src/core/recovery.js +121 -33
  218. package/src/core/save-accepted.js +33 -0
  219. package/src/core/save-core.js +106 -31
  220. package/src/core/save-job.js +108 -0
  221. package/src/core/save-prepare.js +53 -0
  222. package/src/core/save-size-notice.js +3 -1
  223. package/src/core/save-slices.js +15 -0
  224. package/src/core/save.js +557 -127
  225. package/src/core/serialize-steps.js +109 -0
  226. package/src/core/snapshot.js +170 -39
  227. package/src/core/source-map.js +83 -38
  228. package/src/core/stale-host-notice.js +1 -0
  229. package/src/core/upgrade-run.js +59 -8
  230. package/src/dom/active-element.js +5 -0
  231. package/src/lib/assets.js +1 -0
  232. package/src/lib/slice.js +65 -0
  233. package/src/lib/uimotion.js +774 -0
  234. package/src/lib/unsaved-state.js +2 -2
  235. package/src/live/fast-lane.js +10 -3
  236. package/src/live/local-lane.js +202 -11
  237. package/src/live/ops.js +16 -0
  238. package/src/live/page-compaction.js +162 -0
  239. package/src/live/server-lane.js +1 -0
  240. package/src/live/store.js +11 -3
  241. package/src/loader-logic.js +3 -1
  242. package/src/loader.js +14 -4
  243. package/src/page/adopt-boot.js +2 -1
  244. package/src/page/awareness.js +33 -2
  245. package/src/page/blocks.js +89 -27
  246. package/src/page/boot-pack.js +1 -1
  247. package/src/page/chart-tables.js +21 -3
  248. package/src/page/chart.js +406 -63
  249. package/src/page/checklist.js +10 -135
  250. package/src/page/checkpoint-proof.js +7 -1
  251. package/src/page/chrome.js +250 -51
  252. package/src/page/clipboard-shape.js +126 -0
  253. package/src/page/drag.js +33 -3
  254. package/src/page/editor.js +136 -25
  255. package/src/page/export-assets.js +357 -0
  256. package/src/page/export-html.js +209 -0
  257. package/src/page/export-markdown.js +361 -0
  258. package/src/page/features/checklist.js +428 -0
  259. package/src/page/features/export-html.js +189 -0
  260. package/src/page/features/export-markdown.js +169 -0
  261. package/src/page/features/import-markdown.js +349 -0
  262. package/src/page/features/media.js +61 -5
  263. package/src/page/features/registry.js +18 -0
  264. package/src/page/features/table.js +373 -83
  265. package/src/page/features/title-cover.js +331 -0
  266. package/src/page/features/title-icon.js +213 -0
  267. package/src/page/features/toc.js +87 -0
  268. package/src/page/format-command.js +25 -1
  269. package/src/page/format-gate.js +68 -18
  270. package/src/page/format-host.js +17 -0
  271. package/src/page/format-native-commands.js +21 -5
  272. package/src/page/image-actions.js +64 -0
  273. package/src/page/import-markdown.js +262 -0
  274. package/src/page/items.js +135 -17
  275. package/src/page/markdown.js +6 -2
  276. package/src/page/media.js +35 -119
  277. package/src/page/placeholder.js +14 -1
  278. package/src/page/projection.js +98 -10
  279. package/src/page/recover.js +10 -12
  280. package/src/page/recovery-ui.js +464 -0
  281. package/src/page/rich/chart.js +63 -7
  282. package/src/page/save-projection.js +28 -0
  283. package/src/page/save-states.js +13 -3
  284. package/src/page/schema.js +19 -3
  285. package/src/page/table-paste.js +3 -1
  286. package/src/page/table-style.js +4 -4
  287. package/src/page/table.js +44 -2
  288. package/src/page/themes/light-1.css +8 -7
  289. package/src/page/themes/light-1.source.css +8 -7
  290. package/src/page/toc-link.js +31 -0
  291. package/src/page/toc.js +5 -0
  292. package/src/page/view-hooks.js +38 -12
  293. package/src/page/view.js +13 -0
  294. package/src/page/ybind/binding.js +88 -5
  295. package/src/page/ybind/bridge.js +187 -20
  296. package/src/page/ybind/containment.js +162 -0
  297. package/src/page/ybind/doc.js +17 -9
  298. package/src/page/ybind/file-debt.js +63 -0
  299. package/src/page/ybind/layout.js +7 -2
  300. package/src/page/ybind/marks.js +1 -1
  301. package/src/page/ybind/moves.js +5 -4
  302. package/src/page/ybind/origins.js +106 -4
  303. package/src/page/ybind/parts.js +8 -1
  304. package/src/page/ybind/provider.js +113 -17
  305. package/src/page/ybind/recovery-proof.js +32 -9
  306. package/src/page/ybind/recovery-replay.js +558 -15
  307. package/src/page/ybind/recovery.js +174 -45
  308. package/src/page/ybind/table.js +67 -11
  309. package/src/page/ybind/walk-delta.js +258 -0
  310. package/src/page/ybind/walk.js +54 -29
  311. package/src/page/ybind/writer.js +1 -1
  312. package/src/plugins/ai-edit.js +13 -9
  313. package/src/plugins/indicator-chip.js +91 -0
  314. package/src/plugins/indicator.js +13 -80
  315. package/src/plugins/live.js +89 -14
  316. package/src/plugins/menu/actions.js +196 -0
  317. package/src/plugins/menu/people.js +279 -0
  318. package/src/plugins/menu/placement.js +121 -0
  319. package/src/plugins/menu/preferences.js +9 -0
  320. package/src/plugins/menu/seo.js +316 -397
  321. package/src/plugins/menu/style.js +171 -0
  322. package/src/plugins/menu/variants.js +296 -0
  323. package/src/plugins/menu.js +186 -183
  324. package/src/plugins/page-view.js +3 -2
  325. package/src/plugins/page.js +249 -44
  326. package/src/plugins/sortable.js +8 -1
  327. package/src/plugins/source.js +6 -0
  328. package/src/plugins/upgrade.js +51 -17
  329. package/src/plugins/upload-ui.js +12 -17
  330. package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
  331. package/src/plugins/visitor-drafts/intro-style.js +7 -2
  332. package/src/plugins/visitor-drafts/intro.js +5 -3
  333. package/src/plugins/visitor-drafts/owner-chip.js +8 -12
  334. package/src/plugins/visitor-drafts/prompt.js +11 -9
  335. package/src/plugins/visitor-drafts/source.js +4 -3
  336. package/src/plugins/visitor-drafts/store.js +138 -9
  337. package/src/plugins/visitor-drafts/ui-memory.js +2 -2
  338. package/src/plugins/visitor-drafts-owner.js +4 -6
  339. package/src/plugins/visitor-drafts.js +5 -3
  340. package/src/recovery/store.js +1 -1
  341. package/src/route/route.js +4 -0
  342. package/src/server/rooms/index.js +5 -1
  343. package/src/server/rooms/live-data-rooms.js +302 -50
  344. package/src/server/rooms/page-compaction-worker.js +12 -0
  345. package/src/server/rooms/page-compactor.js +92 -0
  346. package/src/server/rooms/page-room-conformance.js +461 -0
  347. package/src/source/parse-steps.js +94 -0
  348. package/src/source/session.js +233 -57
  349. package/src/sync/editing-sender.js +12 -0
  350. package/src/sync/live-sync-edit.js +22 -2
  351. package/src/sync/live-sync.js +150 -23
  352. package/src/sync/page-relay.js +52 -0
  353. package/src/sync/presence-store.js +44 -4
  354. package/src/sync/presence.js +6 -3
  355. package/src/sync/saved-stream.js +11 -6
  356. package/src/ui/bevel-controls.js +1 -0
  357. package/src/ui/bevel-dialog.js +5 -2
  358. package/src/ui/editor-ui.js +77 -0
  359. package/src/ui/page-colour.js +281 -0
  360. package/src/ui/toast.js +16 -7
  361. package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
  362. package/src/vendor/hyper-undo.vendor.js +1 -1
  363. package/src/vendor/hypercms.vendor.js +9 -9
  364. package/src/vendor/parse5.vendor.js +2 -2
  365. package/src/vendor/sap-core.vendor.js +5 -5
  366. package/dist/m/c-2SPVQSHC.js +0 -1
  367. package/dist/m/c-336SGU5W.js +0 -1
  368. package/dist/m/c-3H6BZDNJ.js +0 -6
  369. package/dist/m/c-4DFEJ2PO.js +0 -3
  370. package/dist/m/c-56QRZTZH.js +0 -1
  371. package/dist/m/c-5J4E7O4Y.js +0 -4
  372. package/dist/m/c-5LKDWQTL.js +0 -19
  373. package/dist/m/c-5V3MOVJT.js +0 -1
  374. package/dist/m/c-6AZOGAPE.js +0 -1
  375. package/dist/m/c-6CRUUXOX.js +0 -5
  376. package/dist/m/c-6HMB24EV.js +0 -1
  377. package/dist/m/c-6KEVEQ7L.js +0 -1
  378. package/dist/m/c-A3E3NIMC.js +0 -1
  379. package/dist/m/c-BQ6LOBEE.js +0 -1
  380. package/dist/m/c-BTDI4Y23.js +0 -1
  381. package/dist/m/c-CJ3A7ZKE.js +0 -1
  382. package/dist/m/c-CQHPGKX2.js +0 -1
  383. package/dist/m/c-DFS4XZJV.js +0 -2
  384. package/dist/m/c-DNENWY4O.js +0 -1
  385. package/dist/m/c-E4LIR5X5.js +0 -1
  386. package/dist/m/c-FDNHHZY5.js +0 -1
  387. package/dist/m/c-FROJUZMV.js +0 -1
  388. package/dist/m/c-G2JPKCTP.js +0 -1
  389. package/dist/m/c-GB6TU7WV.js +0 -1
  390. package/dist/m/c-GFJJEBTU.js +0 -1
  391. package/dist/m/c-GJAEGGXY.js +0 -1
  392. package/dist/m/c-GOWBDM6J.js +0 -1
  393. package/dist/m/c-HFKUVACH.js +0 -1
  394. package/dist/m/c-HHDFCNHV.js +0 -1
  395. package/dist/m/c-IEM4NTCS.js +0 -1
  396. package/dist/m/c-JDCX26UP.js +0 -25
  397. package/dist/m/c-KGVGROZT.js +0 -1
  398. package/dist/m/c-KQ6TYJAP.js +0 -135
  399. package/dist/m/c-LFPHCXY3.js +0 -1
  400. package/dist/m/c-LRPUO2MX.js +0 -1
  401. package/dist/m/c-LWLBHLVC.js +0 -3
  402. package/dist/m/c-M2L47S2X.js +0 -7
  403. package/dist/m/c-MFP2ZWD4.js +0 -1
  404. package/dist/m/c-MKSQ4Y57.js +0 -1
  405. package/dist/m/c-NUFFJVVJ.js +0 -1
  406. package/dist/m/c-NUHSPT5P.js +0 -1
  407. package/dist/m/c-O72HVYSA.js +0 -1
  408. package/dist/m/c-OIBUTDXI.js +0 -1
  409. package/dist/m/c-OW5QIGG3.js +0 -1
  410. package/dist/m/c-PGRCK73F.js +0 -1
  411. package/dist/m/c-Q7NQD65V.js +0 -1
  412. package/dist/m/c-QH63LQOS.js +0 -1
  413. package/dist/m/c-R7IWVINQ.js +0 -1
  414. package/dist/m/c-RM7J7BV2.js +0 -1
  415. package/dist/m/c-S4ZEH46B.js +0 -9
  416. package/dist/m/c-SDAXDM2D.js +0 -1
  417. package/dist/m/c-SMGU7RT3.js +0 -235
  418. package/dist/m/c-SPIORRYS.js +0 -1
  419. package/dist/m/c-SWLRT66R.js +0 -1
  420. package/dist/m/c-TCJTVAFN.js +0 -1
  421. package/dist/m/c-TQEMVJCS.js +0 -1
  422. package/dist/m/c-TYLXX4NF.js +0 -21
  423. package/dist/m/c-U62OB4BT.js +0 -1
  424. package/dist/m/c-U6YWUAR3.js +0 -8
  425. package/dist/m/c-UDCSU5G5.js +0 -2
  426. package/dist/m/c-VI5RMSVY.js +0 -1
  427. package/dist/m/c-VRZN56XY.js +0 -1
  428. package/dist/m/c-W5MCHEGA.js +0 -1
  429. package/dist/m/c-WNYBLIOZ.js +0 -1
  430. package/dist/m/c-WWXIDH4O.js +0 -28
  431. package/dist/m/c-XCNTK76U.js +0 -1
  432. package/dist/m/c-XMS6DPBR.js +0 -1
  433. package/dist/m/c-ZUCOKOHT.js +0 -1
  434. package/dist/m/c-ZVPVXTJF.js +0 -4
  435. package/dist/m/c-ZVRKXKM7.js +0 -1
  436. package/dist/m/core/autosave-HRYGUWV6.js +0 -1
  437. package/dist/m/core/conflict-notice-6MROJN2L.js +0 -1
  438. package/dist/m/core/is-edit-mode-KTWA3O5N.js +0 -1
  439. package/dist/m/core/recovery-FLTCC2OC.js +0 -1
  440. package/dist/m/core/save-HQZ4S667.js +0 -1
  441. package/dist/m/core/save-core-KGQWFUNH.js +0 -1
  442. package/dist/m/core/snapshot-EGQZNT45.js +0 -1
  443. package/dist/m/core/unsaved-warning-IDJLD66H.js +0 -1
  444. package/dist/m/loader-S4I7GKXR.js +0 -1
  445. package/dist/m/manifest-4C419D67.js +0 -1
  446. package/dist/m/manifest-edit-B077F288.js +0 -1
  447. package/dist/m/plugins/ai-edit-QN4PMMNL.js +0 -6
  448. package/dist/m/plugins/indicator-URDBBOQO.js +0 -1
  449. package/dist/m/plugins/live-HFWD4OIH.js +0 -1
  450. package/dist/m/plugins/menu-3HIEQSI6.js +0 -138
  451. package/dist/m/plugins/page-JOKB6XPR.js +0 -2
  452. package/dist/m/plugins/page-view-NXOJHNW6.js +0 -1
  453. package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
  454. package/dist/m/plugins/sortable-GWT4PWDV.js +0 -1
  455. package/dist/m/plugins/source-4ZRSAIYM.js +0 -1
  456. package/dist/m/plugins/undo-VDA5P7FH.js +0 -1
  457. package/dist/m/plugins/upgrade-W7FG5VFG.js +0 -1
  458. package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +0 -21
  459. package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +0 -1
  460. package/dist/m/reader-core-DXYCURFF.js +0 -68
  461. package/dist/m/route/route-KOCZSEGN.js +0 -1
  462. package/dist/m/sync/live-sync-J7VMYTKS.js +0 -1
  463. package/dist/m/ui/index-3JLVI27O.js +0 -1
  464. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
  465. /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
  466. /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
  467. /package/dist/m/plugins/{richclay-NRQDSWX6.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
@@ -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
- pairCaptured,
60
+ pairCapturedSteps,
61
+ pairSteps,
58
62
  render,
59
- verify,
60
- verifyForSave
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
- return (receiptBytes !== null ? Promise.resolve(receiptBytes) : fetchSource(sourceUrl)).then((src) => {
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 m = model(src);
232
- const refused = checkSource(m, document);
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 (state.adoptions !== generation) {
240
- if (attempt < SUPERSEDED_RETRIES) return install(sourceUrl, context, attempt + 1, render, latest);
241
- throw new Error('source refused: a save replaced the file while the fetch was in flight');
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
- refreshNow();
300
- state.lastOutcome = null;
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
- ? verifyForSave(out.text, today, document, clone, state.model.parseErrors)
316
- : verify(out.text, today, document, clone, state.model.parseErrors);
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
- state.lastRenderMs = out.ms;
319
- state.lastBytes = out.text.length;
320
- state.lastOutcome = print.size
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
- if (v.model) offerCandidate(v.model, out.text, clone, capture);
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, clone, capture) {
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(refreshNow, { timeout: 2000 });
510
- else setTimeout(refreshNow, 0);
609
+ if (typeof requestIdleCallback === 'function') requestIdleCallback(refreshSoon, { timeout: 2000 });
610
+ else setTimeout(refreshSoon, 0);
511
611
  }
512
612
 
513
613
  /**
514
- * Run a queued refresh now. A no-op when none is queued, which is also what makes the
515
- * idle callback of a refresh that `text()` or `locate()` already ran harmless.
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
- if (!state.installed) return;
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 = model(bytes);
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
- const { map, stats } = pairAgainstPage(m);
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 dispose = () => sync.stop();
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
  },