@panphora/clayjs 1.17.0 → 1.18.1

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-26C55578.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
@@ -21,6 +21,11 @@
21
21
  * verify(out, today) reparse and compare trees before anything is sent
22
22
  * adopt(bytes) re-model and re-pair against what the host accepted
23
23
  *
24
+ * EVERY EXPENSIVE WALK IS A GENERATOR. `modelSteps`, `pairCapturedSteps`, `vDiffSteps`,
25
+ * `vParseErrorsSteps` and the verifier built from them yield between whole units of work,
26
+ * and the synchronous functions are `drain` of those generators (`src/lib/slice.js`). One
27
+ * walk, so a sliced run and a one-shot run cannot disagree about what this module says.
28
+ *
24
29
  * THE FLOOR IS TODAY'S BEHAVIOUR. Anything that does not verify is sent as the full
25
30
  * serialization instead, and counted. A fetch that does not return this document, a
26
31
  * source whose shape outside <html> disagrees with the live document, a render that
@@ -37,13 +42,17 @@
37
42
  * of this code wrote `by <a>Ana</a> <a>Bo</a>` back as `AnaBo` with a green check.
38
43
  */
39
44
 
40
- import { parse } from '../vendor/parse5.vendor.js';
45
+ import { parseSteps } from '../source/parse-steps.js';
46
+ import { drain } from '../lib/slice.js';
41
47
 
42
48
  const RAW_TEXT = new Set(['script', 'style', 'xmp', 'iframe', 'noembed', 'noframes', 'plaintext', 'noscript']);
43
49
  const RCDATA = new Set(['textarea', 'title']);
44
50
  const textContext = (tag) => (RAW_TEXT.has(tag) ? 'raw' : RCDATA.has(tag) ? 'rcdata' : 'normal');
45
51
  const VOID = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
46
52
  const XHTML = 'http://www.w3.org/1999/xhtml';
53
+ // Nodes one walk compares between two yields. It only decides where a sliced walk may
54
+ // pause; the walk itself is the same walk at any value.
55
+ const YIELD_EVERY = 1500;
47
56
 
48
57
  const escText = (s) => s.replace(/&/g, '&amp;').replace(/ /g, '&nbsp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
49
58
  const escAttr = (s) => s.replace(/&/g, '&amp;').replace(/ /g, '&nbsp;').replace(/"/g, '&quot;');
@@ -53,16 +62,21 @@ const escAttr = (s) => s.replace(/&/g, '&amp;').replace(/ /g, '&nbsp;').replace
53
62
  // =============================================================================
54
63
 
55
64
  export function model(src) {
56
- // The error counts come out of the SAME parse, so they cost nothing here. They are
57
- // the only evidence of input a parser throws away, which a tree comparison cannot
58
- // see by construction: a duplicate attribute is discarded during parsing, so a render
59
- // that wrote one twice reparsed to exactly the right tree. That is how the same
60
- // `xmlns:xlink` was added to a file on every save with the verifier green each time.
61
- const parseErrors = new Map();
62
- const doc = parse(src, {
63
- sourceCodeLocationInfo: true,
64
- onParseError: (e) => parseErrors.set(e.code, (parseErrors.get(e.code) || 0) + 1),
65
- });
65
+ return drain(modelSteps(src));
66
+ }
67
+
68
+ /**
69
+ * The same model, parsed in slices so a large document does not hold the thread for the
70
+ * whole parse.
71
+ *
72
+ * The error counts come out of the SAME parse, so they cost nothing here. They are the
73
+ * only evidence of input a parser throws away, which a tree comparison cannot see by
74
+ * construction: a duplicate attribute is discarded during parsing, so a render that wrote
75
+ * one twice reparsed to exactly the right tree. That is how the same `xmlns:xlink` was
76
+ * added to a file on every save with the verifier green each time.
77
+ */
78
+ export function* modelSteps(src) {
79
+ const { document: doc, errors: parseErrors } = yield* parseSteps(src, { sourceCodeLocationInfo: true });
66
80
  return modelFromParsed(src, doc, parseErrors);
67
81
  }
68
82
 
@@ -350,10 +364,26 @@ function keysOfLive(n, keyMap) {
350
364
  keyMap.set(n, k);
351
365
  return k;
352
366
  }
353
- function keysOfLoc(loc) {
367
+ function* keysOfLiveSteps(n, keyMap, walked) {
368
+ if (++walked.n % YIELD_EVERY === 0) yield;
369
+ let k;
370
+ if (n.nodeType === 3) k = { tag: '#text', shallow: 't:' + n.data, content: 't:' + n.data, deep: 't:' + n.data };
371
+ else if (n.nodeType === 8) k = { tag: '#comment', shallow: 'c:' + n.data, content: 'c:' + n.data, deep: 'c:' + n.data };
372
+ else {
373
+ const kids = [];
374
+ for (const c of liveKids(n)) kids.push(yield* keysOfLiveSteps(c, keyMap, walked));
375
+ const shallow = 'e:' + n.localName + '|' + liveAttrsKey(n);
376
+ k = { tag: n.localName, shallow, content: 'e:' + n.localName + '|' + hashKey(kids.map((c) => c.content)), deep: shallow + '|' + hashKey(kids.map((c) => c.deep)) };
377
+ }
378
+ keyMap.set(n, k);
379
+ return k;
380
+ }
381
+ function* keysOfLocSteps(loc, walked) {
382
+ if (++walked.n % YIELD_EVERY === 0) yield;
354
383
  if (loc.kind === 'text') return (loc.keys = { tag: '#text', shallow: 't:' + loc.value, content: 't:' + loc.value, deep: 't:' + loc.value });
355
384
  if (loc.kind === 'comment') return (loc.keys = { tag: '#comment', shallow: 'c:' + loc.value, content: 'c:' + loc.value, deep: 'c:' + loc.value });
356
- const kids = loc.children.map(keysOfLoc);
385
+ const kids = [];
386
+ for (const c of loc.children) kids.push(yield* keysOfLocSteps(c, walked));
357
387
  const shallow = 'e:' + loc.tag + '|' + srcAttrsKey(loc);
358
388
  return (loc.keys = { tag: loc.tag, shallow, content: 'e:' + loc.tag + '|' + hashKey(kids.map((c) => c.content)), deep: shallow + '|' + hashKey(kids.map((c) => c.deep)) });
359
389
  }
@@ -493,10 +523,16 @@ function describeLoc(l) {
493
523
  * @param {Function} [resolve] - node -> the identity to key the map by (default: itself)
494
524
  */
495
525
  export function pair(root, m, resolve = (n) => n) {
526
+ return drain(pairSteps(root, m, resolve));
527
+ }
528
+
529
+ /** The same pairing pass, `yield`ing every `YIELD_EVERY` nodes hashed or bound. */
530
+ export function* pairSteps(root, m, resolve = (n) => n) {
496
531
  const map = new WeakMap();
497
532
  const keyMap = new Map();
498
- keysOfLive(root, keyMap);
499
- keysOfLoc(m.root);
533
+ const walked = { n: 0 };
534
+ yield* keysOfLiveSteps(root, keyMap, walked);
535
+ yield* keysOfLocSteps(m.root, walked);
500
536
  const stats = { paired: 0, unmatchedLive: [], unmatchedSource: [], attrDiffs: [], textDiffs: 0, unresolved: 0 };
501
537
  // A token identifying THIS pairing pass, stamped on the model and on every loc that
502
538
  // binds. render trusts a loc only when the two still agree, which rules out a loc
@@ -505,11 +541,12 @@ export function pair(root, m, resolve = (n) => n) {
505
541
  // model, so holding the node here would keep every node the page has since deleted.
506
542
  const gen = {};
507
543
  m.gen = gen;
508
- bind(root, m.root, map, keyMap, stats, 'html', resolve, gen);
544
+ yield* bindSteps(root, m.root, map, keyMap, stats, 'html', resolve, gen, walked);
509
545
  return { map, stats };
510
546
  }
511
547
 
512
- function bind(node, loc, map, keyMap, stats, path, resolve, gen) {
548
+ function* bindSteps(node, loc, map, keyMap, stats, path, resolve, gen, walked) {
549
+ if (++walked.n % YIELD_EVERY === 0) yield;
513
550
  const key = resolve(node);
514
551
  if (key) { map.set(key, loc); loc.gen = gen; stats.paired++; }
515
552
  else stats.unresolved++;
@@ -520,7 +557,7 @@ function bind(node, loc, map, keyMap, stats, path, resolve, gen) {
520
557
  const usedL = new Set(), usedS = new Set();
521
558
  for (const [i, j] of pairs) {
522
559
  usedL.add(i); usedS.add(j);
523
- bind(L[i], S[j], map, keyMap, stats, path + '>' + (L[i].nodeType === 1 ? L[i].localName : '#') + '[' + i + ']', resolve, gen);
560
+ yield* bindSteps(L[i], S[j], map, keyMap, stats, path + '>' + (L[i].nodeType === 1 ? L[i].localName : '#') + '[' + i + ']', resolve, gen, walked);
524
561
  }
525
562
  for (let i = 0; i < L.length; i++) if (!usedL.has(i)) stats.unmatchedLive.push({ path, node: describe(L[i]) });
526
563
  for (let j = 0; j < S.length; j++) if (!usedS.has(j)) stats.unmatchedSource.push({ path, node: describeLoc(S[j]) });
@@ -547,9 +584,14 @@ function bind(node, loc, map, keyMap, stats, path, resolve, gen) {
547
584
  * @returns {?{map: WeakMap, stats: Object}} null when the trees are not the same shape
548
585
  */
549
586
  export function pairCaptured(root, m, provenance = (n) => n) {
587
+ return drain(pairCapturedSteps(root, m, provenance));
588
+ }
589
+
590
+ /** The same pairing walk, `yield`ing every `YIELD_EVERY` nodes compared. */
591
+ export function* pairCapturedSteps(root, m, provenance = (n) => n) {
550
592
  if (!m.root) return null;
551
593
  const found = [];
552
- const unresolved = matchCaptured(root, m.root, provenance, found);
594
+ const unresolved = yield* matchCapturedSteps(root, m.root, provenance, found, { n: 0 });
553
595
  if (unresolved < 0) return null;
554
596
  const map = new WeakMap();
555
597
  // One token for this pass, set on the model and on every bound loc, exactly as `pair`
@@ -567,7 +609,8 @@ export function pairCaptured(root, m, provenance = (n) => n) {
567
609
  }
568
610
 
569
611
  /** @returns {number} unresolved nodes below this pair, or -1 when the two differ at all */
570
- function matchCaptured(node, loc, provenance, found) {
612
+ function* matchCapturedSteps(node, loc, provenance, found, compared) {
613
+ if (++compared.n % YIELD_EVERY === 0) yield;
571
614
  if (loc.kind === 'text') {
572
615
  if (node.nodeType !== 3 || node.data !== loc.value) return -1;
573
616
  } else if (loc.kind === 'comment') {
@@ -583,7 +626,7 @@ function matchCaptured(node, loc, provenance, found) {
583
626
  const L = capturedKids(node), S = loc.children;
584
627
  if (L.length !== S.length) return -1;
585
628
  for (let i = 0; i < L.length; i++) {
586
- const n = matchCaptured(L[i], S[i], provenance, found);
629
+ const n = yield* matchCapturedSteps(L[i], S[i], provenance, found, compared);
587
630
  if (n < 0) return -1;
588
631
  unresolved += n;
589
632
  }
@@ -1027,7 +1070,7 @@ function lis(vals) {
1027
1070
  // the right one.
1028
1071
 
1029
1072
  export function verify(rendered, today, liveDocument, clone = null, sourceErrors = null) {
1030
- return verifyRendered(rendered, today, liveDocument, clone, sourceErrors, false);
1073
+ return drain(verifyRenderedSteps(rendered, today, liveDocument, clone, sourceErrors, false));
1031
1074
  }
1032
1075
 
1033
1076
  /**
@@ -1044,10 +1087,14 @@ export function verify(rendered, today, liveDocument, clone = null, sourceErrors
1044
1087
  * same walk, told to retain what it parsed.
1045
1088
  */
1046
1089
  export function verifyForSave(rendered, today, liveDocument, clone = null, sourceErrors = null) {
1047
- return verifyRendered(rendered, today, liveDocument, clone, sourceErrors, true);
1090
+ return drain(verifyForSaveSteps(rendered, today, liveDocument, clone, sourceErrors));
1091
+ }
1092
+
1093
+ export function* verifyForSaveSteps(rendered, today, liveDocument, clone = null, sourceErrors = null) {
1094
+ return yield* verifyRenderedSteps(rendered, today, liveDocument, clone, sourceErrors, true);
1048
1095
  }
1049
1096
 
1050
- function verifyRendered(rendered, today, liveDocument, clone, sourceErrors, retain) {
1097
+ export function* verifyRenderedSteps(rendered, today, liveDocument, clone, sourceErrors, retain) {
1051
1098
  const P = new DOMParser();
1052
1099
  const A = P.parseFromString(rendered, 'text/html');
1053
1100
  const outsideA = vOutside(A), outsideLive = vOutside(liveDocument);
@@ -1060,17 +1107,17 @@ function verifyRendered(rendered, today, liveDocument, clone, sourceErrors, reta
1060
1107
  let cloneDiff = null;
1061
1108
  if (clone) {
1062
1109
  at = [];
1063
- cloneDiff = vDiff(A.documentElement, clone, 'html', at);
1110
+ cloneDiff = yield* vDiffSteps(A.documentElement, clone, 'html', at);
1064
1111
  if (!cloneDiff) {
1065
- const w = vParseErrors(rendered, sourceErrors, retain);
1112
+ const w = yield* vParseErrorsSteps(rendered, sourceErrors, retain);
1066
1113
  if (w.diff) return { ok: false, diff: w.diff };
1067
1114
  return retain ? withCandidate({ ok: true, diff: null, oracle: 'clone' }, w, rendered, liveDocument)
1068
1115
  : { ok: true, diff: null, oracle: 'clone' };
1069
1116
  }
1070
1117
  }
1071
1118
  const B = P.parseFromString(today, 'text/html');
1072
- const diff = vDiff(A.documentElement, B.documentElement, 'html');
1073
- const worse = diff ? null : vParseErrors(rendered, sourceErrors, retain);
1119
+ const diff = yield* vDiffSteps(A.documentElement, B.documentElement, 'html');
1120
+ const worse = diff ? null : yield* vParseErrorsSteps(rendered, sourceErrors, retain);
1074
1121
  if (!diff) {
1075
1122
  if (worse.diff) return { ok: false, diff: worse.diff };
1076
1123
  return retain ? withCandidate({ ok: true, diff: null, oracle: 'today' }, worse, rendered, liveDocument)
@@ -1114,12 +1161,9 @@ export function nodeAt(root, at) {
1114
1161
  * represent, such as a duplicated element, change the tree, and the comparison above
1115
1162
  * is what catches those.
1116
1163
  */
1117
- function vParseErrors(rendered, sourceErrors, retain) {
1164
+ export function* vParseErrorsSteps(rendered, sourceErrors, retain) {
1118
1165
  if (!sourceErrors) return { diff: null };
1119
- const seen = new Map();
1120
- const doc = parse(rendered, retain
1121
- ? { sourceCodeLocationInfo: true, onParseError: (e) => seen.set(e.code, (seen.get(e.code) || 0) + 1) }
1122
- : { onParseError: (e) => seen.set(e.code, (seen.get(e.code) || 0) + 1) });
1166
+ const { document: doc, errors: seen } = yield* parseSteps(rendered, retain ? { sourceCodeLocationInfo: true } : {});
1123
1167
  for (const [code, n] of seen) {
1124
1168
  const was = sourceErrors.get(code) || 0;
1125
1169
  if (n > was) return { diff: `the render introduced parse errors the source does not have: ${code} ${was} -> ${n}` };
@@ -1164,7 +1208,7 @@ function vOutside(doc) {
1164
1208
  * still changes the tree its markup parses to. Recursion terminates because each step
1165
1209
  * compares strictly less markup than the one above it.
1166
1210
  */
1167
- function vNoscript(a, b, path) {
1211
+ function* vNoscriptSteps(a, b, path, compared) {
1168
1212
  const P = new DOMParser();
1169
1213
  // Read each side the way it was parsed, which verify fixes, not the node. `a` is always
1170
1214
  // the render reparsed by DOMParser, with scripting off, so its <noscript> text is TEXT,
@@ -1177,7 +1221,7 @@ function vNoscript(a, b, path) {
1177
1221
  const kids = Array.from(el.childNodes);
1178
1222
  return kids.length && kids.every((n) => n.nodeType === 3) ? kids.map((n) => n.data).join('') : el.innerHTML;
1179
1223
  };
1180
- return vDiff(P.parseFromString(a.innerHTML, 'text/html').body, P.parseFromString(raw(b), 'text/html').body, path + '>#noscript');
1224
+ return yield* vDiffSteps(P.parseFromString(a.innerHTML, 'text/html').body, P.parseFromString(raw(b), 'text/html').body, path + '>#noscript', [], compared);
1181
1225
  }
1182
1226
  function vKids(el) {
1183
1227
  const list = el.localName === 'template' && el.content ? el.content.childNodes : el.childNodes;
@@ -1197,17 +1241,18 @@ function sameAttrsInOrder(a, b) {
1197
1241
  }
1198
1242
  return true;
1199
1243
  }
1200
- function vDiff(a, b, path, at = []) {
1244
+ export function* vDiffSteps(a, b, path, at = [], compared = { n: 0 }) {
1245
+ if (++compared.n % YIELD_EVERY === 0) yield;
1201
1246
  if (a.nodeType !== b.nodeType) return path + ': node type ' + a.nodeType + ' vs ' + b.nodeType;
1202
1247
  if (a.nodeType === 3 || a.nodeType === 8) return a.data === b.data ? null : path + ': text ' + JSON.stringify(a.data.slice(0, 60)) + ' vs ' + JSON.stringify(b.data.slice(0, 60));
1203
1248
  if (a.localName !== b.localName || a.namespaceURI !== b.namespaceURI) return path + ': tag ' + a.localName + ' vs ' + b.localName;
1204
1249
  if (!sameAttrsInOrder(a, b) && vAttrs(a) !== vAttrs(b)) return path + ': attrs ' + vAttrs(a) + ' vs ' + vAttrs(b);
1205
- if (a.localName === 'noscript' && a.namespaceURI === XHTML) return vNoscript(a, b, path);
1250
+ if (a.localName === 'noscript' && a.namespaceURI === XHTML) return yield* vNoscriptSteps(a, b, path, compared);
1206
1251
  const ka = vKids(a), kb = vKids(b);
1207
1252
  if (ka.length !== kb.length) return path + ': ' + ka.length + ' vs ' + kb.length + ' children';
1208
1253
  for (let i = 0; i < ka.length; i++) {
1209
1254
  at.push(i);
1210
- const d = vDiff(ka[i], kb[i], path + '>' + (ka[i].localName || '#') + '[' + i + ']', at);
1255
+ const d = yield* vDiffSteps(ka[i], kb[i], path + '>' + (ka[i].localName || '#') + '[' + i + ']', at, compared);
1211
1256
  if (d) return d;
1212
1257
  at.pop();
1213
1258
  }
@@ -68,6 +68,7 @@ function build() {
68
68
 
69
69
  document.body.appendChild(root);
70
70
  place();
71
+ ui.paintLook(root, { background: ui.TOKENS.surface });
71
72
  window.visualViewport?.addEventListener("resize", place);
72
73
  window.visualViewport?.addEventListener("scroll", place);
73
74
  }
@@ -8,6 +8,8 @@ const MANIFEST_FAILED = "The new version could not be downloaded. Nothing was ch
8
8
  const SAVE_FAILED = "The upgrade could not be saved. Nothing was changed.";
9
9
  const CONFLICT = "This document changed somewhere else. Reload and try again.";
10
10
  const BUSY = "Something else is changing this document. Nothing was changed.";
11
+ const UNDECLARED = "This document does not say which parts are its data, so it cannot be upgraded safely. Nothing was changed.";
12
+ const PAGE_LOST = "The new version has no place for this page's content. Nothing was changed.";
11
13
 
12
14
  async function digestOf(bytes) {
13
15
  const subtle = globalThis.crypto && globalThis.crypto.subtle;
@@ -116,6 +118,26 @@ async function declaredRenames(htmlText) {
116
118
  return mod && mod.renames && typeof mod.renames === "object" ? mod.renames : {};
117
119
  }
118
120
 
121
+ // The page editor's content is its article and the live stores beside it, not values a
122
+ // rules tag can name: the upgrade carries them whole into the new version's article.
123
+ const PAGE_STORE = 'script[clay~="live"][merge]';
124
+
125
+ function carryPage(doc, afterDoc, article) {
126
+ const target = afterDoc.querySelector("article[data-page]");
127
+ if (!target) return null;
128
+ target.replaceWith(afterDoc.importNode(article, true));
129
+ for (const store of doc.querySelectorAll(PAGE_STORE)) {
130
+ const name = store.getAttribute("merge");
131
+ if (name !== "page" && !name.startsWith("page.")) continue;
132
+ const copy = afterDoc.importNode(store, true);
133
+ const existing = [...afterDoc.querySelectorAll(PAGE_STORE)].find((el) => el.getAttribute("merge") === name);
134
+ if (existing) existing.replaceWith(copy);
135
+ else afterDoc.querySelector("article[data-page]").after(copy);
136
+ }
137
+ const doctype = afterDoc.doctype ? `<!DOCTYPE ${afterDoc.doctype.name}>\n` : "";
138
+ return doctype + afterDoc.documentElement.outerHTML;
139
+ }
140
+
119
141
  export async function prepare(update, {
120
142
  doc = document,
121
143
  loc = location,
@@ -187,13 +209,23 @@ export async function prepare(update, {
187
209
  }
188
210
  const afterDoc = new DOMParser().parseFromString(result.html, "text/html");
189
211
  const before = upgrade.extractAll(doc).dataByName;
212
+ const article = doc.querySelector("article[data-page]");
213
+ if (!article && !Object.keys(before || {}).length) {
214
+ return { ok: false, code: "undeclared", message: UNDECLARED };
215
+ }
216
+ let html = result.html;
217
+ if (article) {
218
+ const carried = carryPage(doc, afterDoc, article);
219
+ if (!carried) return { ok: false, code: "lost", lost: [], discarded: 0, message: PAGE_LOST };
220
+ html = carried;
221
+ }
190
222
  const after = upgrade.extractAll(afterDoc).dataByName;
191
223
  const lost = lostValues(before, after, renames);
192
224
  const discarded = result.summary && result.summary.totals ? result.summary.totals.discarded || 0 : 0;
193
225
  if (lost.length || discarded > 0) {
194
226
  return { ok: false, code: "lost", lost, discarded, message: lossMessage(lost, discarded) };
195
227
  }
196
- return { ok: true, html: result.html, version: result.toVersion || latest.version };
228
+ return { ok: true, html, version: result.toVersion || latest.version };
197
229
  }
198
230
 
199
231
  function lossMessage(lost, discarded) {
@@ -249,7 +281,17 @@ async function applyUnderSlot(update, { doc, loc, fetchFn, clay, reload, replace
249
281
  }
250
282
  }
251
283
 
252
- const prepared = await prepare(update, { doc, loc, fetchFn });
284
+ // An editing tab's live article holds the editor, not the page: the upgrade starts from
285
+ // the document as a save writes it.
286
+ let source = doc;
287
+ if (pageClay.isEditMode && typeof pageClay.getHTML === "function") {
288
+ try {
289
+ source = new DOMParser().parseFromString(await pageClay.getHTML(), "text/html");
290
+ } catch {
291
+ return { ok: false, code: "failed", message: SAVE_FAILED };
292
+ }
293
+ }
294
+ const prepared = await prepare(update, { doc: source, loc, fetchFn });
253
295
  if (!prepared.ok) return prepared;
254
296
 
255
297
  const save = typeof replace === "function"
@@ -294,11 +336,20 @@ function downloadName(loc) {
294
336
  return /\.html?$/i.test(name) ? name : `${name}.html`;
295
337
  }
296
338
 
297
- export function downloadCopy({ clay, doc = document, loc = location } = {}) {
298
- const getHTML = clay && clay.getHTML;
299
- if (typeof getHTML !== "function" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return false;
339
+ // A view-mode tab has no save capture, and edits nothing, so the copy is the stored file
340
+ // this upgrade is about to replace. Only a host without version history asks for it.
341
+ export async function pageBytes(clay, { loc = location, fetchFn = (url, init) => fetch(url, init) } = {}) {
342
+ if (typeof clay.getHTML === "function") return clay.getHTML();
343
+ const res = await fetchFn(loc.href, { cache: "no-store", credentials: "same-origin" });
344
+ if (!res.ok) throw new Error(`copy fetch ${res.status}`);
345
+ return res.text();
346
+ }
347
+
348
+ export async function downloadCopy({ clay, doc = document, loc = location, fetchFn } = {}) {
349
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return false;
300
350
  try {
301
- const url = URL.createObjectURL(new Blob([getHTML()], { type: "text/html" }));
351
+ const bytes = await pageBytes(clay, { loc, fetchFn });
352
+ const url = URL.createObjectURL(new Blob([bytes], { type: "text/html" }));
302
353
  const a = doc.createElement("a");
303
354
  a.setAttribute("clay", "editor-ui");
304
355
  a.href = url;
@@ -371,8 +422,8 @@ export async function start(update, options = {}) {
371
422
  };
372
423
 
373
424
  if (download) {
374
- download.addEventListener("click", () => {
375
- if (downloadCopy({ clay, doc, loc: options.loc })) {
425
+ download.addEventListener("click", async () => {
426
+ if (await downloadCopy({ clay, doc, loc: options.loc, fetchFn: options.fetchFn })) {
376
427
  agree.disabled = false;
377
428
  status.textContent = "Your copy was downloaded.";
378
429
  } else {
@@ -0,0 +1,5 @@
1
+ export function deepActiveElement(scope = document) {
2
+ let active = scope.activeElement;
3
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
4
+ return active;
5
+ }
package/src/lib/assets.js CHANGED
@@ -10,6 +10,7 @@ const ASSETS = {
10
10
  "highlight/json": () => import("../vendor/hljs-json.vendor.js"),
11
11
  "highlight/python": () => import("../vendor/hljs-python.vendor.js"),
12
12
  "highlight/xml": () => import("../vendor/hljs-xml.vendor.js"),
13
+ uimotion: () => import("./uimotion.js"),
13
14
  };
14
15
 
15
16
  export function createAssets(table) {
@@ -0,0 +1,65 @@
1
+ /**
2
+ * slice.js — a generator that can be run twice two ways.
3
+ *
4
+ * Every expensive walk in save verification is written once, as a generator that yields
5
+ * between whole units of work. `drain` runs it to completion in the calling task, which
6
+ * is the synchronous function this module had before; `runSliced` resumes it and hands
7
+ * the event loop back whenever a slice has spent its budget, so the same code can also
8
+ * run across tasks. There is one implementation, so the two cannot drift apart.
9
+ *
10
+ * YIELD POINTS ARE COOPERATIVE, NOT PREEMPTIVE. A generator yields only between units of
11
+ * work the walk itself chose, so nothing outside sees a half-built model or a half-paired
12
+ * map: an abort runs the generator's `finally` blocks and returns `{ aborted: true }`
13
+ * with no result at all, and the caller must treat a partial walk as no walk.
14
+ *
15
+ * LAZY. Nothing imports this from the editor's ready path; only source modules that
16
+ * already load parse5 do.
17
+ */
18
+
19
+ /** Run a generator to completion in this task and return its value. */
20
+ export function drain(gen) {
21
+ let step = gen.next();
22
+ while (!step.done) step = gen.next();
23
+ return step.value;
24
+ }
25
+
26
+ const viaTimer = () => new Promise((resolve) => setTimeout(resolve, 0));
27
+
28
+ /** A task boundary that works in a hidden tab, where timers are throttled. */
29
+ const viaChannel = () => new Promise((resolve) => {
30
+ if (typeof MessageChannel !== "function") { viaTimer().then(resolve); return; }
31
+ const channel = new MessageChannel();
32
+ channel.port1.onmessage = () => { channel.port1.close(); resolve(); };
33
+ channel.port2.postMessage(null);
34
+ });
35
+
36
+ export const defaultYieldTask = () => {
37
+ const scheduler = globalThis.scheduler;
38
+ return scheduler?.yield ? scheduler.yield() : viaChannel();
39
+ };
40
+
41
+ const monotonic = () => (globalThis.performance ? globalThis.performance.now() : Date.now());
42
+
43
+ /**
44
+ * Resume a generator, yielding to a task whenever a slice has spent `budgetMs`.
45
+ *
46
+ * Resolves with the generator's own return value, rejects with its exception. When
47
+ * `signal` aborts, the generator is closed (`gen.return()`, so `finally` blocks run) and
48
+ * the result is `{ aborted: true }`; nothing partial is handed back.
49
+ */
50
+ export async function runSliced(gen, {
51
+ budgetMs = 4,
52
+ signal,
53
+ now = monotonic,
54
+ yieldTask = defaultYieldTask,
55
+ } = {}) {
56
+ let sliceStart = now();
57
+ for (;;) {
58
+ if (signal?.aborted) { gen.return(undefined); return { aborted: true }; }
59
+ const step = gen.next();
60
+ if (step.done) return step.value;
61
+ if (now() - sliceStart < budgetMs) continue;
62
+ await yieldTask();
63
+ sliceStart = now();
64
+ }
65
+ }