@panphora/clayjs 1.23.0 → 1.24.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 (471) hide show
  1. package/dist/all.js +1 -1
  2. package/dist/blockeditor/themes/light-1.css +431 -0
  3. package/dist/clay-dom.js +1 -1
  4. package/dist/clay-events.js +1 -1
  5. package/dist/clay-internals.js +1 -1
  6. package/dist/clay-options.js +1 -1
  7. package/dist/clay-router.js +1 -1
  8. package/dist/clay-ui.js +1 -1
  9. package/dist/clay-utils.js +1 -1
  10. package/dist/clay.js +1 -1
  11. package/dist/clay.standalone.js +30285 -26613
  12. package/dist/m/c-277QP5ET.js +1 -0
  13. package/dist/m/c-2BLVA6IN.js +5 -0
  14. package/dist/m/c-2OJMOUPC.js +1 -0
  15. package/dist/m/c-2PVGJFI5.js +1 -0
  16. package/dist/m/{c-AYVGD6A6.js → c-3UW5EJ77.js} +1 -1
  17. package/dist/m/{c-T5NK2TVY.js → c-42ZNV7LI.js} +1 -1
  18. package/dist/m/{c-3TLG3OLG.js → c-46LJAZZJ.js} +1 -1
  19. package/dist/m/c-47IBJGPQ.js +1 -0
  20. package/dist/m/c-5FXEKU4Y.js +1 -0
  21. package/dist/m/c-5KXDKC5G.js +1 -0
  22. package/dist/m/{c-EUGJVHDD.js → c-5RZNFTWD.js} +1 -1
  23. package/dist/m/c-65LJU2H2.js +72 -0
  24. package/dist/m/{c-NQJRVJ56.js → c-6IJEVE6B.js} +1 -1
  25. package/dist/m/{c-NW54NHKE.js → c-6LEYVQJK.js} +1 -1
  26. package/dist/m/{c-GSRLXYWL.js → c-6Q6RGFBF.js} +1 -1
  27. package/dist/m/c-6RGEQNVV.js +1 -0
  28. package/dist/m/c-7CM4GMTS.js +6 -0
  29. package/dist/m/c-7NCWENHV.js +1 -0
  30. package/dist/m/c-7V6MGMX2.js +1 -0
  31. package/dist/m/{c-LKSL4AUJ.js → c-7WZSUTYP.js} +1 -1
  32. package/dist/m/c-A4L7APIU.js +1 -0
  33. package/dist/m/c-APGHGDZM.js +1 -0
  34. package/dist/m/c-AT7GLQGV.js +34 -0
  35. package/dist/m/{c-ZRXJD4G6.js → c-B7GOFGQR.js} +1 -1
  36. package/dist/m/{c-KDXIJEEF.js → c-BGN7RCTJ.js} +1 -1
  37. package/dist/m/c-BKM46LUJ.js +1 -0
  38. package/dist/m/c-BOMQIQ3V.js +1 -0
  39. package/dist/m/{c-62HZW5DH.js → c-C5AKJVRH.js} +1 -1
  40. package/dist/m/{c-BXYSDKAN.js → c-CJ7QYEWI.js} +1 -1
  41. package/dist/m/c-CTGRDT6E.js +1 -0
  42. package/dist/m/c-CV4T65JE.js +6 -0
  43. package/dist/m/c-CVSWLX3X.js +1 -0
  44. package/dist/m/c-CZHTXGJS.js +1 -0
  45. package/dist/m/c-D7NT5S4F.js +1 -0
  46. package/dist/m/c-DD26VXAP.js +1 -0
  47. package/dist/m/{c-ANNFZFLN.js → c-DDS23EY6.js} +1 -1
  48. package/dist/m/{c-ALDVFPWL.js → c-DFKBGSN2.js} +1 -1
  49. package/dist/m/c-DIKR7H57.js +147 -0
  50. package/dist/m/c-DXPOFLEJ.js +1 -0
  51. package/dist/m/c-EDE45NM7.js +1 -0
  52. package/dist/m/c-EILLN2H2.js +1 -0
  53. package/dist/m/c-EODTTNVA.js +1 -0
  54. package/dist/m/{c-G6COVIZB.js → c-ESK4S67I.js} +1 -1
  55. package/dist/m/c-FDWR3FL7.js +1 -0
  56. package/dist/m/{c-WQZTI5P2.js → c-FEC6LGFF.js} +1 -1
  57. package/dist/m/c-FLEIU6CG.js +1 -0
  58. package/dist/m/c-FQAQRXGK.js +1 -0
  59. package/dist/m/{c-W4JCVLAB.js → c-FSQXFG5T.js} +1 -1
  60. package/dist/m/c-FVPNX2GO.js +135 -0
  61. package/dist/m/c-FYDKYW6E.js +1 -0
  62. package/dist/m/c-GNI7WHKF.js +32 -0
  63. package/dist/m/c-GO635CRU.js +3 -0
  64. package/dist/m/c-H3FNLBHN.js +2 -0
  65. package/dist/m/c-HDVH3MVC.js +72 -0
  66. package/dist/m/c-HIX5H5L7.js +1 -0
  67. package/dist/m/c-HKSLOVU4.js +1 -0
  68. package/dist/m/{c-SGRIYOAJ.js → c-HL6BLJNA.js} +1 -1
  69. package/dist/m/c-HTLYXCUH.js +1 -0
  70. package/dist/m/{c-QIR5CAKM.js → c-IUL7PU4P.js} +1 -1
  71. package/dist/m/c-IXO7EI2Q.js +1 -0
  72. package/dist/m/{c-W5E5P2TZ.js → c-JQF2EUCG.js} +1 -1
  73. package/dist/m/{c-NEL7HQW3.js → c-JWDCQL6J.js} +1 -1
  74. package/dist/m/c-KQKKIVLW.js +20 -0
  75. package/dist/m/c-LATNQ6Q5.js +1 -0
  76. package/dist/m/c-LD6UVV5R.js +1 -0
  77. package/dist/m/c-LS7IVQVW.js +1 -0
  78. package/dist/m/{c-JCOQVZYS.js → c-LSVN3OLG.js} +1 -1
  79. package/dist/m/{c-JDZDF2QM.js → c-LTSPHVAK.js} +1 -1
  80. package/dist/m/c-MFOSMAVM.js +3 -0
  81. package/dist/m/c-MJ7GVE46.js +1 -0
  82. package/dist/m/c-MP6S4AZU.js +6 -0
  83. package/dist/m/c-MQKSKQHG.js +2 -0
  84. package/dist/m/{c-UHUNXWKN.js → c-MQYEDF3Q.js} +1 -1
  85. package/dist/m/{c-IHUWUKIU.js → c-MR75YPRT.js} +1 -1
  86. package/dist/m/c-ND3Q2G2I.js +1 -0
  87. package/dist/m/c-NEQRFC4O.js +1 -0
  88. package/dist/m/{c-5FKRZNJV.js → c-NFQFV7OP.js} +1 -1
  89. package/dist/m/c-NHXOBD6A.js +1 -0
  90. package/dist/m/{c-QIKT6SKM.js → c-NICICT4P.js} +1 -1
  91. package/dist/m/c-NQAPWM7O.js +1 -0
  92. package/dist/m/{c-QUGCKMWF.js → c-NVU3DORS.js} +1 -1
  93. package/dist/m/{c-LWIDDWAR.js → c-OCYG4NBQ.js} +22 -22
  94. package/dist/m/c-OH2FPYSY.js +19 -0
  95. package/dist/m/c-OYHI67B6.js +1 -0
  96. package/dist/m/{c-R2XUODVY.js → c-OYW5MFPZ.js} +1 -1
  97. package/dist/m/{c-5B4YBZXC.js → c-P2HKMJAW.js} +1 -1
  98. package/dist/m/{c-TSBVMWAH.js → c-PCNJETUA.js} +1 -1
  99. package/dist/m/c-PDBNCJCT.js +1 -0
  100. package/dist/m/c-PGSBXHUE.js +1 -0
  101. package/dist/m/{c-5TIZ2I64.js → c-PYG2NGWU.js} +1 -1
  102. package/dist/m/c-Q26XHBLQ.js +1 -0
  103. package/dist/m/c-Q6BG3ZLO.js +1 -0
  104. package/dist/m/c-QENKN7QI.js +1 -0
  105. package/dist/m/c-QQFHEODI.js +1 -0
  106. package/dist/m/c-QTEUO2K6.js +1 -0
  107. package/dist/m/c-R23UHMXG.js +1 -0
  108. package/dist/m/c-RETTCOWO.js +10 -0
  109. package/dist/m/c-SE3SIY7Y.js +7 -0
  110. package/dist/m/{c-XX75FFNL.js → c-SGGGCIVD.js} +1 -1
  111. package/dist/m/c-SGH3UTF3.js +1 -0
  112. package/dist/m/c-SIM7Z3U7.js +1 -0
  113. package/dist/m/{c-X5LNHET5.js → c-SKMIR5HQ.js} +1 -1
  114. package/dist/m/{c-R27DWQKX.js → c-STRI73GQ.js} +1 -1
  115. package/dist/m/c-SUXHUL7U.js +1 -0
  116. package/dist/m/c-SXGEWPFC.js +10 -0
  117. package/dist/m/c-SXNSSHPP.js +1 -0
  118. package/dist/m/c-T3P3AXEW.js +1 -0
  119. package/dist/m/{c-PXLTUYZC.js → c-TFLRT5JX.js} +1 -1
  120. package/dist/m/{c-YCLU4UNT.js → c-TGIMBAWE.js} +1 -1
  121. package/dist/m/c-TGSHHFO6.js +1 -0
  122. package/dist/m/c-U4NTUVDZ.js +1 -0
  123. package/dist/m/c-UOOIF2AL.js +1 -0
  124. package/dist/m/{c-6TYGD5AB.js → c-UOUUI265.js} +1 -1
  125. package/dist/m/c-V2U3F4QX.js +1 -0
  126. package/dist/m/c-V2V5FKXX.js +1 -0
  127. package/dist/m/c-VBXRXTVS.js +26 -0
  128. package/dist/m/c-VHSMPITY.js +1 -0
  129. package/dist/m/c-VPXCXBDT.js +1 -0
  130. package/dist/m/c-WCG6AKA7.js +1 -0
  131. package/dist/m/c-WNOLMHI2.js +1 -0
  132. package/dist/m/{c-CRPIDGXC.js → c-X6MOYATI.js} +1 -1
  133. package/dist/m/{c-FQGHR32O.js → c-X75G43SZ.js} +1 -1
  134. package/dist/m/c-XB6EAIAR.js +13 -0
  135. package/dist/m/c-XMIDHEKF.js +1 -0
  136. package/dist/m/c-XUZMRQ7O.js +4 -0
  137. package/dist/m/{c-2Z22FCFK.js → c-XW5TIYYB.js} +1 -1
  138. package/dist/m/c-YDW4VCHP.js +1 -0
  139. package/dist/m/{c-QGAPG7OM.js → c-YLHTD3KX.js} +1 -1
  140. package/dist/m/c-YNIEIPBS.js +1 -0
  141. package/dist/m/c-Z4G2UK6T.js +4 -0
  142. package/dist/m/c-Z6MROEQ4.js +1 -0
  143. package/dist/m/c-ZFO5RW6G.js +2 -0
  144. package/dist/m/c-ZGCO4J3H.js +8 -0
  145. package/dist/m/c-ZKKAF4UT.js +3 -0
  146. package/dist/m/c-ZRGEN6QQ.js +4 -0
  147. package/dist/m/core/{admin-attrs-U7IJNYBG.js → admin-attrs-ONRWVXPK.js} +1 -1
  148. package/dist/m/core/{autosave-F5YQCKVL.js → autosave-TBKAAAFB.js} +1 -1
  149. package/dist/m/core/conflict-notice-FOEE6I7E.js +1 -0
  150. package/dist/m/core/draft-mode-4MCL3LYI.js +1 -0
  151. package/dist/m/core/{host-permission-6EVJGNPL.js → host-permission-X4BENZEU.js} +1 -1
  152. package/dist/m/core/{is-edit-mode-GCX6767E.js → is-edit-mode-FFHCQ5DR.js} +1 -1
  153. package/dist/m/core/{persist-CJXIL7D4.js → persist-6ILUR34S.js} +1 -1
  154. package/dist/m/core/{recovery-DRFZRJED.js → recovery-APWADU4K.js} +1 -1
  155. package/dist/m/core/save-6HT2LABE.js +1 -0
  156. package/dist/m/core/save-core-EVMNZQ4V.js +1 -0
  157. package/dist/m/core/site-edits-G4BOZMV4.js +1 -0
  158. package/dist/m/core/{unsaved-warning-HNQVTA2D.js → unsaved-warning-ITZDIZ2I.js} +1 -1
  159. package/dist/m/internals/{index-NRPNDTSA.js → index-HS3CQDFW.js} +1 -1
  160. package/dist/m/loader-GLLVDUZA.js +1 -0
  161. package/dist/m/manifest-50FF1C45.js +1 -0
  162. package/dist/m/manifest-edit-BE1A621D.js +1 -0
  163. package/dist/m/manifest.json +2072 -1811
  164. package/dist/m/page/editor-KAZDJNND.js +307 -0
  165. package/dist/m/plugins/ai-edit-LABECCSN.js +6 -0
  166. package/dist/m/plugins/{cms-H7DTMUQX.js → cms-T2HLI7JG.js} +16 -16
  167. package/dist/m/plugins/{demo-42BSYQ7T.js → demo-T7B4RIKL.js} +1 -1
  168. package/dist/m/plugins/{derive-QVI3LI3A.js → derive-6LPJOCAV.js} +1 -1
  169. package/dist/m/plugins/indicator-J4ABFARX.js +1 -0
  170. package/dist/m/plugins/{live-DCUBKXY4.js → live-PGBIEMGB.js} +1 -1
  171. package/dist/m/plugins/menu-YZL3X2AJ.js +1 -0
  172. package/dist/m/plugins/page-WOCNX2IN.js +2 -0
  173. package/dist/m/plugins/page-view-SJODOOGE.js +1 -0
  174. package/dist/m/plugins/{people-5XRNRP5U.js → people-C4TCWFOQ.js} +1 -1
  175. package/dist/m/plugins/{quickcrop-WWF5I422.js → quickcrop-X5MRIBAP.js} +2 -2
  176. package/dist/m/plugins/richclay-Y2NV44NA.js +549 -0
  177. package/dist/m/plugins/sortable-XCHQG65Z.js +1 -0
  178. package/dist/m/plugins/{source-6BKMK4PO.js → source-YK7MO23W.js} +1 -1
  179. package/dist/m/plugins/{undo-3JEYK5QY.js → undo-VLRBQNPN.js} +1 -1
  180. package/dist/m/plugins/upgrade-NNTJBSK2.js +1 -0
  181. package/dist/m/plugins/{upload-TTCQRIM5.js → upload-ZKKJV76X.js} +1 -1
  182. package/dist/m/plugins/visitor-drafts-RTZNONHT.js +20 -0
  183. package/dist/m/plugins/{visitor-drafts-owner-Z5B2OQBR.js → visitor-drafts-owner-LHUHFU3L.js} +1 -1
  184. package/dist/m/plugins/{visitor-menu-4LQMKLPP.js → visitor-menu-FY4KM2SW.js} +1 -1
  185. package/dist/m/plugins/wire-M2NR354P.js +1 -0
  186. package/dist/m/{reader-core-POFQCKRV.js → reader-core-T2S3JC4E.js} +2 -2
  187. package/dist/m/sync/live-sync-Z64YREWQ.js +1 -0
  188. package/dist/m/ui/index-MGTWOW2C.js +1 -0
  189. package/dist/m/vendor/{sap-core.vendor-OA5SKL7Q.js → sap-core.vendor-QAEPMETX.js} +1 -1
  190. package/dist/offline-manifest.json +1250 -1082
  191. package/dist/server/core.cjs +132 -109
  192. package/dist/server/page-compaction-worker.cjs +5 -5
  193. package/dist/server/rooms.mjs +3 -1
  194. package/docs/guide.md +89 -44
  195. package/entries/clay-data.js +1 -1
  196. package/entries/sap.js +1 -1
  197. package/package.json +5 -3
  198. package/src/blockeditor/themes/content.css +70 -0
  199. package/src/blockeditor/themes/light-1.css +431 -0
  200. package/src/blockeditor/themes/light-1.source.css +270 -0
  201. package/src/blockeditor/themes/light-2.css +211 -0
  202. package/src/blockeditor/themes/light-3.css +130 -0
  203. package/src/blockeditor/themes/light-4.css +299 -0
  204. package/src/core/conflict-notice.js +28 -0
  205. package/src/core/draft-mode.js +6 -0
  206. package/src/core/host-meta.js +6 -6
  207. package/src/core/is-edit-mode.js +9 -3
  208. package/src/core/mode-support.js +3 -0
  209. package/src/core/mode-switch.js +4 -4
  210. package/src/core/mode-transition.js +3 -1
  211. package/src/core/page-format-wire.js +1 -1
  212. package/src/core/page-format.js +8 -8
  213. package/src/core/save-accepted.js +6 -7
  214. package/src/core/save-core.js +52 -23
  215. package/src/core/save-failure-notice.js +37 -0
  216. package/src/core/save-feedback.js +6 -0
  217. package/src/core/save-replace.js +1 -1
  218. package/src/core/save-size.js +2 -2
  219. package/src/core/save.js +78 -15
  220. package/src/core/site-edits.js +166 -0
  221. package/src/core/unsaved-warning.js +2 -2
  222. package/src/core/upgrade-run.js +4 -4
  223. package/src/lib/ai-comment.js +23 -0
  224. package/src/lib/effect.js +8 -0
  225. package/src/lib/idb-error-code.js +20 -0
  226. package/src/live/fast-lane.js +10 -10
  227. package/src/live/local-lane.js +10 -10
  228. package/src/live/page-compaction.js +8 -8
  229. package/src/live/server-lane.js +1 -1
  230. package/src/loader-logic.js +48 -18
  231. package/src/loader.js +11 -6
  232. package/src/page/adopt-boot.js +1 -1
  233. package/src/page/adopt.js +2 -2
  234. package/src/page/ai-area.js +182 -0
  235. package/src/page/ai-preview.js +80 -0
  236. package/src/page/ai-surface.js +257 -0
  237. package/src/page/ai-tool.js +308 -145
  238. package/src/page/announce.js +2 -2
  239. package/src/page/awareness.js +18 -18
  240. package/src/page/blocks.js +35 -34
  241. package/src/page/blocksel.js +2 -2
  242. package/src/page/board-drag.js +11 -11
  243. package/src/page/board-face.js +1 -1
  244. package/src/page/board-panel.js +26 -26
  245. package/src/page/board-style.js +127 -127
  246. package/src/page/board-view.js +48 -48
  247. package/src/page/chart.js +29 -29
  248. package/src/page/checkpoint-proof.js +19 -19
  249. package/src/page/chrome.js +74 -65
  250. package/src/page/clipboard-shape.js +1 -1
  251. package/src/page/csv-import.js +4 -4
  252. package/src/page/drag.js +5 -5
  253. package/src/page/editor.js +301 -296
  254. package/src/page/export-html.js +8 -8
  255. package/src/page/features/checklist.js +36 -36
  256. package/src/page/features/export-html.js +19 -19
  257. package/src/page/features/export-markdown.js +10 -10
  258. package/src/page/features/import-markdown.js +13 -13
  259. package/src/page/features/media.js +50 -50
  260. package/src/page/features/table.js +17 -17
  261. package/src/page/features/title-cover.js +17 -17
  262. package/src/page/features/title-icon.js +7 -7
  263. package/src/page/features/toc.js +1 -1
  264. package/src/page/features/tool.js +42 -18
  265. package/src/page/format-command.js +4 -4
  266. package/src/page/format-gate.js +6 -6
  267. package/src/page/format-host.js +6 -6
  268. package/src/page/format-level.js +3 -3
  269. package/src/page/format-native-commands.js +2 -2
  270. package/src/page/highlight-slot.js +1 -1
  271. package/src/page/highlight.js +4 -4
  272. package/src/page/history-intent.js +2 -2
  273. package/src/page/image-actions.js +4 -4
  274. package/src/page/link.js +10 -10
  275. package/src/page/media.js +3 -3
  276. package/src/page/mention-picker.js +7 -7
  277. package/src/page/merge.js +1 -1
  278. package/src/page/placeholder.js +3 -3
  279. package/src/page/projection.js +11 -11
  280. package/src/page/reader-patch.js +2 -2
  281. package/src/page/recover.js +26 -26
  282. package/src/page/recovery-ui.js +28 -28
  283. package/src/page/rich/chart.js +1 -1
  284. package/src/page/rich/page-tool.js +21 -6
  285. package/src/page/rich/tool-runtime.js +3 -2
  286. package/src/page/save-projection.js +2 -2
  287. package/src/page/save-states.js +9 -9
  288. package/src/page/schema.js +9 -9
  289. package/src/page/table-style.js +21 -21
  290. package/src/page/toc-link.js +1 -1
  291. package/src/page/tool-capability.js +8 -3
  292. package/src/page/tool-transport.js +52 -0
  293. package/src/page/tool-views.js +4 -0
  294. package/src/page/tools.js +6 -1
  295. package/src/page/view.js +6 -6
  296. package/src/page/ybind/binding.js +1 -1
  297. package/src/page/ybind/conversion.js +1 -1
  298. package/src/page/ybind/layout.js +4 -4
  299. package/src/page/ybind/migrate.js +1 -1
  300. package/src/page/ybind/origins.js +2 -2
  301. package/src/page/ybind/provider.js +2 -2
  302. package/src/page/ybind/recovery-proof.js +3 -3
  303. package/src/page/ybind/recovery-replay.js +2 -2
  304. package/src/plugins/ai-edit/picker.js +235 -0
  305. package/src/plugins/ai-edit.js +220 -34
  306. package/src/plugins/indicator-chip.js +39 -34
  307. package/src/plugins/indicator.js +13 -4
  308. package/src/plugins/live.js +21 -13
  309. package/src/plugins/menu/actions.js +89 -17
  310. package/src/plugins/menu/seo.js +32 -6
  311. package/src/plugins/menu/shell-style.js +26 -7
  312. package/src/plugins/menu/shell.js +12 -7
  313. package/src/plugins/menu/variants.js +6 -10
  314. package/src/plugins/menu/visitor.js +44 -34
  315. package/src/plugins/menu.js +3 -2
  316. package/src/plugins/page-view.js +2 -2
  317. package/src/plugins/page.js +257 -98
  318. package/src/plugins/sortable.js +7 -0
  319. package/src/plugins/upgrade.js +2 -1
  320. package/src/plugins/visitor-drafts/commit.js +303 -0
  321. package/src/plugins/visitor-drafts/idb.js +221 -0
  322. package/src/plugins/visitor-drafts/intro-style.js +1 -1
  323. package/src/plugins/visitor-drafts/intro.js +8 -25
  324. package/src/plugins/visitor-drafts/prompt.js +30 -4
  325. package/src/plugins/visitor-drafts/session.js +345 -0
  326. package/src/plugins/visitor-drafts/store.js +351 -32
  327. package/src/plugins/visitor-drafts.js +158 -27
  328. package/src/server/checkpoint/index.js +8 -8
  329. package/src/server/rooms/index.js +2 -2
  330. package/src/server/rooms/live-data-rooms.js +35 -35
  331. package/src/server/rooms/live-page-history.js +2 -2
  332. package/src/server/rooms/page-admission.js +34 -6
  333. package/src/server/rooms/page-conversion.js +1 -1
  334. package/src/server/rooms/page-lineage.js +4 -4
  335. package/src/server/rooms/page-rebase.js +4 -4
  336. package/src/server/rooms/page-room-conformance.js +14 -14
  337. package/src/server/rooms/page-state.js +2 -2
  338. package/src/server/rooms/page-write-admission.js +7 -7
  339. package/src/server/rooms/page-write-guard.js +3 -3
  340. package/src/sync/data-guard-notice.js +265 -0
  341. package/src/sync/live-sync.js +54 -11
  342. package/src/sync/page-relay.js +4 -4
  343. package/src/sync/presence-store.js +1 -1
  344. package/src/sync/presence-target.js +3 -3
  345. package/src/sync/presence.js +1 -1
  346. package/src/sync/stream.js +54 -13
  347. package/src/sync/wire-profile.js +8 -3
  348. package/src/ui/bevel.js +1 -1
  349. package/src/ui/index.js +9 -10
  350. package/src/ui/skins/cms.js +1 -1
  351. package/src/ui/skins/quickcrop.js +1 -1
  352. package/src/ui/skins/richclay.js +2 -2
  353. package/src/ui/toast-lazy.js +12 -3
  354. package/src/ui/toast.js +9 -3
  355. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  356. package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
  357. package/src/vendor/hypercms.vendor.js +16 -16
  358. package/src/vendor/richclay.vendor.js +48 -20
  359. package/src/vendor/sap-core.vendor.js +1 -1
  360. package/tool-generation/context.md +44 -0
  361. package/tool-generation/system.md +23 -0
  362. package/dist/m/c-24PMUJIV.js +0 -2
  363. package/dist/m/c-2AYVYEI4.js +0 -1
  364. package/dist/m/c-2AZDZWCK.js +0 -1
  365. package/dist/m/c-2L6O5PDS.js +0 -6
  366. package/dist/m/c-2RYOGUQM.js +0 -2
  367. package/dist/m/c-2ZVOEU4M.js +0 -1
  368. package/dist/m/c-37NAONZA.js +0 -1
  369. package/dist/m/c-3GJ4SDE2.js +0 -1
  370. package/dist/m/c-3O7OBDCM.js +0 -1
  371. package/dist/m/c-5OJ7YF2N.js +0 -1
  372. package/dist/m/c-5WMXEKFH.js +0 -135
  373. package/dist/m/c-6RDOKLN7.js +0 -1
  374. package/dist/m/c-A4IP6MBC.js +0 -1
  375. package/dist/m/c-A6L3FMUS.js +0 -1
  376. package/dist/m/c-ATKAOH23.js +0 -1
  377. package/dist/m/c-B6O7AJYG.js +0 -32
  378. package/dist/m/c-BCGWJYXW.js +0 -1
  379. package/dist/m/c-BGKAT2FL.js +0 -7
  380. package/dist/m/c-BYTKL6UX.js +0 -1
  381. package/dist/m/c-DHBRA6DL.js +0 -1
  382. package/dist/m/c-DTMZ3ZHI.js +0 -1
  383. package/dist/m/c-E3ZXARWA.js +0 -1
  384. package/dist/m/c-EK63G3CG.js +0 -1
  385. package/dist/m/c-ELYIIYRS.js +0 -1
  386. package/dist/m/c-EXKHPOVC.js +0 -3
  387. package/dist/m/c-FCSZ3DC5.js +0 -20
  388. package/dist/m/c-FTWGYI5E.js +0 -1
  389. package/dist/m/c-G4PWUDD5.js +0 -1
  390. package/dist/m/c-GBKB5WDL.js +0 -2
  391. package/dist/m/c-GBTVCJXC.js +0 -1
  392. package/dist/m/c-GC2HICFJ.js +0 -1
  393. package/dist/m/c-GRWMD6O5.js +0 -1
  394. package/dist/m/c-HCNWJE4A.js +0 -4
  395. package/dist/m/c-HG2C77BM.js +0 -4
  396. package/dist/m/c-JGHEACGK.js +0 -1
  397. package/dist/m/c-JGLX6TBK.js +0 -1
  398. package/dist/m/c-JNRKHYWO.js +0 -1
  399. package/dist/m/c-KBS3UV6B.js +0 -1
  400. package/dist/m/c-KQ7E3PYY.js +0 -1
  401. package/dist/m/c-L3F5ZLIV.js +0 -1
  402. package/dist/m/c-L4KMT7FO.js +0 -6
  403. package/dist/m/c-L5VFATT6.js +0 -1
  404. package/dist/m/c-LCYPMULG.js +0 -1
  405. package/dist/m/c-LIGQYZ7M.js +0 -6
  406. package/dist/m/c-LKIWEPAV.js +0 -1
  407. package/dist/m/c-MPSKIU2I.js +0 -1
  408. package/dist/m/c-N7JPUPGC.js +0 -1
  409. package/dist/m/c-NK33MMYF.js +0 -1
  410. package/dist/m/c-NQLWOHLH.js +0 -10
  411. package/dist/m/c-NTHUTABN.js +0 -1
  412. package/dist/m/c-NWX3MFT4.js +0 -1
  413. package/dist/m/c-OKXI52N7.js +0 -1
  414. package/dist/m/c-OPONJ2UK.js +0 -128
  415. package/dist/m/c-OWQWOGYI.js +0 -1
  416. package/dist/m/c-P6Q2ZTFQ.js +0 -1
  417. package/dist/m/c-QEN6HZCA.js +0 -1
  418. package/dist/m/c-QIXT3Q7C.js +0 -28
  419. package/dist/m/c-QM5LXRDT.js +0 -9
  420. package/dist/m/c-QYYUVLVG.js +0 -1
  421. package/dist/m/c-RQRMPZXD.js +0 -1
  422. package/dist/m/c-SBCBXKOZ.js +0 -26
  423. package/dist/m/c-SKF5HL4I.js +0 -4
  424. package/dist/m/c-SRPC3VBE.js +0 -1
  425. package/dist/m/c-TANBH7FI.js +0 -1
  426. package/dist/m/c-THGQQDYY.js +0 -1
  427. package/dist/m/c-UD5JZTDL.js +0 -1
  428. package/dist/m/c-UDGEFWNU.js +0 -1
  429. package/dist/m/c-UGNV5MFB.js +0 -1
  430. package/dist/m/c-VIRFF5HG.js +0 -8
  431. package/dist/m/c-VJYEVRYK.js +0 -1
  432. package/dist/m/c-VQCEOGKH.js +0 -13
  433. package/dist/m/c-VYJ7O4RI.js +0 -34
  434. package/dist/m/c-W7IPVCAA.js +0 -1
  435. package/dist/m/c-WBGWGOM7.js +0 -1
  436. package/dist/m/c-WJ5DRBHT.js +0 -72
  437. package/dist/m/c-X5WC4PX5.js +0 -1
  438. package/dist/m/c-XCFQEWW6.js +0 -3
  439. package/dist/m/c-XGH4GS2T.js +0 -1
  440. package/dist/m/c-XLQ3BDMM.js +0 -1
  441. package/dist/m/c-ZQXNHZZT.js +0 -72
  442. package/dist/m/core/conflict-notice-42H4ASLA.js +0 -1
  443. package/dist/m/core/draft-mode-WSQAT67S.js +0 -1
  444. package/dist/m/core/save-ESPSQPLV.js +0 -1
  445. package/dist/m/core/save-core-6SQHOR6W.js +0 -1
  446. package/dist/m/loader-FBDE2UAB.js +0 -1
  447. package/dist/m/manifest-1D2F5CA5.js +0 -1
  448. package/dist/m/manifest-edit-F7CC5CFD.js +0 -1
  449. package/dist/m/page/editor-OSKOW6RS.js +0 -304
  450. package/dist/m/plugins/ai-edit-VV4QTINH.js +0 -6
  451. package/dist/m/plugins/indicator-WVPGMOZ7.js +0 -1
  452. package/dist/m/plugins/menu-Q4MFFQ6E.js +0 -1
  453. package/dist/m/plugins/page-OAWUJQEL.js +0 -2
  454. package/dist/m/plugins/page-view-DZ26ZDDQ.js +0 -1
  455. package/dist/m/plugins/richclay-PGOOJ4OK.js +0 -517
  456. package/dist/m/plugins/sortable-266HY6TR.js +0 -1
  457. package/dist/m/plugins/upgrade-BNZVFHMZ.js +0 -1
  458. package/dist/m/plugins/visitor-drafts-2AUYKAIJ.js +0 -20
  459. package/dist/m/plugins/wire-W2EV36KZ.js +0 -1
  460. package/dist/m/sync/live-sync-XGUPHI3I.js +0 -1
  461. package/dist/m/ui/index-VGYAKAKI.js +0 -1
  462. package/dist/page/themes/light-1.css +0 -431
  463. package/src/page/themes/content.css +0 -70
  464. package/src/page/themes/light-1.css +0 -431
  465. package/src/page/themes/light-1.source.css +0 -270
  466. package/src/page/themes/light-2.css +0 -211
  467. package/src/page/themes/light-3.css +0 -130
  468. package/src/page/themes/light-4.css +0 -299
  469. /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
  470. /package/dist/m/plugins/{quickcrop-WWF5I422.js.LEGAL.txt → quickcrop-X5MRIBAP.js.LEGAL.txt} +0 -0
  471. /package/dist/m/plugins/{richclay-PGOOJ4OK.js.LEGAL.txt → richclay-Y2NV44NA.js.LEGAL.txt} +0 -0
@@ -1,17 +1,38 @@
1
1
  import { isDraftMode, isEditMode, isOwner } from "../core/is-edit-mode.js";
2
2
  import { hasSaveToken } from "../core/host-attrs.js";
3
- import { draftSlot, setDraftTransport, setDraftFlush } from "../core/draft-mode.js";
4
- import { createStore, adoptLegacy, draftPath, legacyKey, safeStorage } from "./visitor-drafts/store.js";
3
+ import { draftSlot, draftStoreFailed, setDraftTransport, setDraftFlush, setDraftUnconfirmed } from "../core/draft-mode.js";
4
+ import { reloadAfterDiscard } from "../core/unsaved-warning.js";
5
+ import { createStore, adoptLegacy, draftPath, legacyKey, safeStorage, storageFailure } from "./visitor-drafts/store.js";
5
6
  import { createUiMemory } from "./visitor-drafts/ui-memory.js";
6
7
  import { rememberSource, downloadUrl } from "./visitor-drafts/source.js";
7
8
  import { mountPrompt } from "./visitor-drafts/prompt.js";
8
9
  import { DOWNLOAD_URL, backToDraft, editOriginal, inertVisitorDrafts } from "./visitor-drafts/inert.js";
9
10
 
10
11
  let store = null;
12
+ // This tab's side of the draft database, once it has opened. Without one, `localStorage`
13
+ // holds the record for this page load, as it did before the database.
14
+ let session = null;
15
+ let direct = true;
16
+ let database = false;
17
+ let opening = Promise.resolve();
18
+ // The database did not open and the full draft is only there: nothing is saved over it
19
+ // for the rest of this page load.
20
+ let refused = false;
11
21
  let restoredDetail = null;
12
22
  let downloadLink = DOWNLOAD_URL;
13
23
  let source = null;
14
24
  let ready = Promise.resolve();
25
+ // Settles once the draft is on the page. Already settled for a first visit and for a
26
+ // draft `localStorage` holds in full; the loader awaits it before the next plugin loads.
27
+ let restoring = Promise.resolve();
28
+ // The synchronous half of a save: false while something this tab changed is neither
29
+ // stored nor in its journal slot.
30
+ let keep = () => true;
31
+ // A promise while the page's last changes are not yet where the download page reads them,
32
+ // null once they are.
33
+ let settle = () => null;
34
+ // Past this deadline the download uses this page's current HTML.
35
+ const DOWNLOAD_WAIT_MS = 3000;
15
36
 
16
37
  // The chip is a convenience: a failed import leaves the draft working without it.
17
38
  function mountChip() {
@@ -41,29 +62,121 @@ const rulesTag = document.querySelector('script[data-rules-name~="api"]');
41
62
  if (isDraftMode && slot && rulesTag) {
42
63
  const path = draftPath();
43
64
  const moved = adoptLegacy(slot, { path });
65
+ const local = safeStorage();
44
66
  store = createStore({ path, fallback: moved === "failed" ? legacyKey(slot) : null });
45
- setDraftTransport((html) => store.write(html));
46
- const result = store.restore();
47
- restoredDetail = { restored: result.restored, hadDraft: result.hadDraft, parked: Boolean(result.parked), ...(result.partial && { partial: true }), ...(result.blocked && { blocked: true }) };
48
- document.dispatchEvent(new CustomEvent("clay:drafts-restored", { detail: restoredDetail }));
49
- const memory = createUiMemory(store.key, safeStorage());
67
+ direct = !(typeof globalThis.indexedDB?.open === "function" && globalThis.IDBKeyRange);
68
+ database = !direct;
69
+ // The session loads beside the database open, off the path of a draft `localStorage` holds
70
+ // in full. Until it is there a save still makes its operation and journals it.
71
+ opening = direct ? Promise.resolve() : import("./visitor-drafts/session.js")
72
+ .then((m) => m.openSession({ store, storage: local, reload: () => reloadAfterDiscard({ isCurrent: () => true, reload: () => window.location.reload() }) }))
73
+ .then((opened) => { session = opened; }, () => { direct = true; });
74
+
75
+ setDraftTransport((html) => {
76
+ const recapture = !restoredDetail;
77
+ return restoring.then(() => {
78
+ if (refused) throw storageFailure(document, new Error("the draft database did not open"));
79
+ if (recapture) html = window.clay.getHTML();
80
+ if (direct) return void store.write(html);
81
+ store.capture(html);
82
+ return opening.then(() => {
83
+ if (session) return session.sync().then(() => {});
84
+ store.write(html);
85
+ });
86
+ });
87
+ });
88
+
89
+ const memory = createUiMemory(store.key, local);
50
90
  source = rememberSource(memory);
51
91
  downloadLink = downloadUrl(source);
52
- const appName = document.querySelector('meta[name="application-name"]')?.content?.trim() || document.title.split(/:|\s\|\s/)[0].trim() || "This app";
53
- const template = document.querySelector("template[visitor-intro]");
54
- const start = () => {
55
- ready = mountPrompt({ memory, appName, link: downloadLink }).then((prompt) => mountIntroLate({ memory, template, appName, hadDraft: result.hadDraft }).then(() => prompt)).then((prompt) => {
56
- document.addEventListener("clay:drafts-store-failed", () => prompt.showError());
57
- document.addEventListener("clay:drafts-saved", () => prompt.clearError());
58
- return (isOwner ? mountChip() : Promise.resolve(null)).then(() => prompt);
59
- });
92
+
93
+ function finish(result) {
94
+ restoredDetail = { restored: result.restored, hadDraft: result.hadDraft, parked: Boolean(result.parked), ...(result.partial && { partial: true }), ...(result.blocked && { blocked: true }) };
95
+ document.dispatchEvent(new CustomEvent("clay:drafts-restored", { detail: restoredDetail }));
96
+ const appName = document.querySelector('meta[name="application-name"]')?.content?.trim() || document.title.split(/:|\s\|\s/)[0].trim() || "This app";
97
+ const template = document.querySelector("template[visitor-intro]");
98
+ const start = () => {
99
+ ready = mountPrompt({ memory, appName, link: downloadLink, settle: () => settle(), canDownloadCurrent: () => !refused && !restoredDetail.blocked }).then((prompt) => mountIntroLate({ memory, template, appName, hadDraft: result.hadDraft || store.kept() }).then(() => prompt)).then((prompt) => {
100
+ document.addEventListener("clay:drafts-store-failed", () => prompt.showError());
101
+ document.addEventListener("clay:drafts-saved", () => prompt.clearError());
102
+ if (draftStoreFailed()) prompt.showError();
103
+ return (isOwner ? mountChip() : Promise.resolve(null)).then(() => prompt);
104
+ });
105
+ };
106
+ document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", start, { once: true }) : start();
107
+ }
108
+
109
+ keep = () => {
110
+ if (refused) return false;
111
+ if (!restoredDetail) return true;
112
+ if (direct) return store.flush();
113
+ const safe = store.keep();
114
+ session?.hide();
115
+ return safe;
116
+ };
117
+ setDraftFlush(keep);
118
+ setDraftUnconfirmed(() => !direct && store.unsafe());
119
+ settle = () => {
120
+ if (refused) return Promise.resolve(false);
121
+ if (!restoredDetail) return restoring.then(() => settle());
122
+ if (direct) {
123
+ return store.flush() ? null : Promise.resolve(false);
124
+ }
125
+ const safe = store.keep();
126
+ if (!store.pending()) return safe ? null : Promise.resolve(false);
127
+ const committed = opening.then(() => session ? session.sync().then(() => true) : store.flush()).catch(() => false);
128
+ let timer;
129
+ return Promise.race([committed, new Promise((resolve) => { timer = setTimeout(() => resolve(false), DOWNLOAD_WAIT_MS); })])
130
+ .finally(() => clearTimeout(timer));
60
131
  };
61
- document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", start, { once: true }) : start();
62
- setDraftFlush(() => store.flush());
63
- window.addEventListener("storage", (event) => { if (event.key === store.key) store.adoptExternal(); });
64
- const flush = () => store.flush();
65
- document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") flush(); });
66
- window.addEventListener("pagehide", flush);
132
+ document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") keep(); });
133
+ window.addEventListener("pagehide", keep);
134
+
135
+ // Another tab's save reaches a page on the database through its session. With
136
+ // `localStorage` as the record it arrives as that key's storage event, once the page
137
+ // editor listens (an event before that is missed), and when the tab comes back from the
138
+ // page cache.
139
+ const takeOtherTab = () => {
140
+ if (!direct || refused || !restoredDetail) return;
141
+ // Read before adopting: adopting reads the record as this tab's own.
142
+ const unit = store.externalUnit();
143
+ // A page another tab wrote goes to the page editor, which merges it into its own.
144
+ if (unit) document.dispatchEvent(new CustomEvent("clay:drafts-blockeditor-external", { detail: { live: unit.live, adopted: () => store.adoptedUnit(unit) } }));
145
+ store.adoptExternal();
146
+ };
147
+ window.addEventListener("storage", (event) => { if (event.key === store.key) takeOtherTab(); });
148
+ document.addEventListener("clay:drafts-blockeditor-listening", takeOtherTab);
149
+ window.addEventListener("pageshow", (event) => { if (event.persisted) takeOtherTab(); });
150
+
151
+ const held = store.held();
152
+ const head = held?.v === 2 && held.idb === true;
153
+ const foreignReset = !direct && held?.v === 1 && held.w === undefined && store.generation() !== "";
154
+ if (head || foreignReset || (!direct && held?.w && held.w.reset !== store.generation())) {
155
+ // The full draft is only in the database, or this copy is from before the last Start
156
+ // over: the page waits for the database's record, and plugins that read the document
157
+ // wait with the page.
158
+ restoring = opening.then(() => session?.first).then((result) => {
159
+ if (result) return finish(store.restore(result.record && { raw: JSON.stringify(result.record), record: result.record }));
160
+ throw new Error("the draft database did not open");
161
+ }).catch((error) => {
162
+ if (!head) {
163
+ if (!foreignReset) { try { local?.removeItem(store.key); } catch {} }
164
+ return finish(store.restore());
165
+ }
166
+ refused = true;
167
+ storageFailure(document, error);
168
+ finish({ restored: false, hadDraft: true, blocked: true });
169
+ });
170
+ } else {
171
+ finish(store.restore());
172
+ // The database did not open: `localStorage` is the record for this page load, and what
173
+ // this tab journaled while it waited is saved there.
174
+ opening.then(() => {
175
+ if (session) return;
176
+ store.release();
177
+ store.flush();
178
+ });
179
+ }
67
180
  } else if (!slot || !rulesTag) {
68
181
  const missing = [];
69
182
  if (!slot) missing.push('a visitor-drafts="<slot>" attribute on <html>');
@@ -73,18 +186,33 @@ if (isDraftMode && slot && rulesTag) {
73
186
  ready = mountChip();
74
187
  }
75
188
 
76
- export { ready };
189
+ export { ready, restoring };
77
190
 
78
191
  export const visitorDrafts = store ? {
79
192
  active: true,
80
193
  slot: store.slot,
81
194
  key: store.key,
82
- hasDraft: () => Boolean(store.read()),
195
+ hasDraft: () => store.kept(),
83
196
  restored: () => restoredDetail,
84
- saveNow: () => store.writeLive(),
197
+ saveNow() {
198
+ if (refused || !restoredDetail) return false;
199
+ if (direct) return store.writeLive();
200
+ try { store.capture(window.clay.getHTML()); } catch { return false; }
201
+ session?.sync().catch(() => {});
202
+ return !store.unsafe();
203
+ },
85
204
  reset() {
86
- store.clear();
87
- window.location.reload();
205
+ if (!database) {
206
+ store.clear();
207
+ window.location.reload();
208
+ return true;
209
+ }
210
+ // The generation ends in the database before the page loads again.
211
+ opening.then(() => {
212
+ if (session) return session.startOver();
213
+ if (direct && !refused) return store.clear();
214
+ throw new Error("the draft database did not open");
215
+ }).then(() => window.location.reload(), (error) => storageFailure(document, error));
88
216
  return true;
89
217
  },
90
218
  onRestore(fn) {
@@ -98,6 +226,9 @@ export const visitorDrafts = store ? {
98
226
  editOriginal,
99
227
  backToDraft,
100
228
  downloadUrl: () => downloadLink,
101
- download: () => ready.then((prompt) => { store.flush(); return prompt?.download(); }),
229
+ download: () => ready.then(async (prompt) => {
230
+ const saved = await settle();
231
+ return prompt?.download(saved === false);
232
+ }),
102
233
  source,
103
234
  } : inertVisitorDrafts();
@@ -9,7 +9,7 @@ import { merge3, mergeJson } from 'hyper-morph';
9
9
  import { mergePageModels } from '../../page/merge.js';
10
10
  import { documentTitle } from '../../page/document-title.js';
11
11
 
12
- const PAGE = /^page(\.[0-9]+)?$/;
12
+ const PAGE = /^blockeditor(\.[0-9]+)?$/;
13
13
  const TITLE_MODE = 'clay-seo-title-mode';
14
14
  const NAME = /^[A-Za-z0-9_.:-]{1,64}$/;
15
15
  const attr = (node, name) => node.attrs.find((item) => item.name === name)?.value;
@@ -25,7 +25,7 @@ function provenanceOf(room, store) {
25
25
  export function captureCheckpoint(room, { nodeId, roomKey }) {
26
26
  if (!room || room.key !== roomKey || String(room.nodeId) !== String(nodeId)) throw new TypeError('The room identity changed');
27
27
  const { epoch, seq } = room;
28
- const generation = room.pageTransition?.id ?? null;
28
+ const generation = room.blockeditorTransition?.id ?? null;
29
29
  const stores = [...room.stores].filter(([, store]) => !store.ephemeral).map(([name, store]) => {
30
30
  const text = serializeLive(store.value);
31
31
  return { name, value: cloneJson(store.value), text, liveAt: stampOf(epoch, seq, text), provenance: provenanceOf(room, store) };
@@ -51,7 +51,7 @@ function inspect(html) {
51
51
  if (!blocked && node.parentNode === head && node.tagName === 'meta' && (attr(node, 'name') ?? '').trim().toLowerCase() === TITLE_MODE) {
52
52
  titleModes.push(attr(node, 'content'));
53
53
  }
54
- if (!blocked && node.tagName === 'article' && attr(node, 'data-page') !== undefined) articles.push(node);
54
+ if (!blocked && node.tagName === 'article' && attr(node, 'data-blockeditor') !== undefined) articles.push(node);
55
55
  if (!blocked && node.tagName === 'script' && attr(node, 'src') === undefined &&
56
56
  (attr(node, 'clay') ?? '').split(/\s+/).includes('live')) {
57
57
  const type = (attr(node, 'type') ?? '').split(';')[0].trim().toLowerCase();
@@ -81,8 +81,8 @@ function startTag(html, node, store) {
81
81
  }
82
82
 
83
83
  function projection(capture) {
84
- const page = capture.stores.find((store) => store.name === 'page');
85
- if (!page || capture.stores.some((store) => PAGE.test(store.name) && store.name !== 'page')) return { kind: 'refused', reason: 'invalid-document' };
84
+ const page = capture.stores.find((store) => store.name === 'blockeditor');
85
+ if (!page || capture.stores.some((store) => PAGE.test(store.name) && store.name !== 'blockeditor')) return { kind: 'refused', reason: 'invalid-document' };
86
86
  try {
87
87
  const blocks = blocksFromYValue(page.value);
88
88
  if (Object.values(blocks).some((block) => block.t === 'tool' || (block.t === 'table' && !Object.keys(block.cols ?? {}).length))) {
@@ -91,8 +91,8 @@ function projection(capture) {
91
91
  return { kind: 'ok', blocks };
92
92
  } catch (error) {
93
93
  if (error instanceof YDecodeError) {
94
- return { kind: 'refused', reason: error.code === 'incomplete-update' || error.code === 'unresolved-update' ? 'incomplete-page'
95
- : error.code === 'unsupported-content' ? 'unknown-page-content' : 'invalid-document' };
94
+ return { kind: 'refused', reason: error.code === 'incomplete-update' || error.code === 'unresolved-update' ? 'incomplete-blockeditor'
95
+ : error.code === 'unsupported-content' ? 'unknown-blockeditor-content' : 'invalid-document' };
96
96
  }
97
97
  throw error;
98
98
  }
@@ -234,7 +234,7 @@ export function renderCheckpoint(currentHtml, capture, { createDocument, recover
234
234
  if (!recovery && found.kind === 'adopt' && capture.adopted !== found.A && !holdsEdit(found, decoded.blocks, doc)) return { kind: 'refused', reason: 'needs-adoption' };
235
235
  const current = currentHtml.slice(articleLoc.startTag.endOffset, articleLoc.endTag.startOffset);
236
236
  rendered = renderBlocks(decoded.blocks, { clean: fragmentCleaner(doc) });
237
- if (current !== rendered.html && holdsEnvelope(current)) return { kind: 'refused', reason: 'unknown-page-content' };
237
+ if (current !== rendered.html && holdsEnvelope(current)) return { kind: 'refused', reason: 'unknown-blockeditor-content' };
238
238
  }
239
239
  catch (error) {
240
240
  if (error instanceof YDecodeError || error instanceof SyntaxError) return { kind: 'refused', reason: 'invalid-document' };
@@ -40,8 +40,8 @@ export {
40
40
  postReceipt,
41
41
  } from './live-provenance.js';
42
42
 
43
- export { isHybridPageValue, isYPageValue, inspectPage, pageBaseline, captureFormat, checkPageFormatAdmission } from './page-admission.js';
44
- export { PAGE_FORMAT, PAGE_FORMAT_HEADER, PAGE_FORMAT_EXTENSION, PAGE_FORMAT_UPGRADE, formatCapability, pageFormatFloor, withPageFormatFloor } from '../../core/page-format.js';
43
+ export { isHybridPageValue, isYPageValue, inspectPage, pageBaseline, captureFormat, checkPageFormatAdmission, oldBlockeditorFormat, oldFormatRefusal } from './page-admission.js';
44
+ export { PAGE_FORMAT, PAGE_FORMAT_HEADER, PAGE_FORMAT_EXTENSION, PAGE_FORMAT_UPGRADE, formatCapability, blockeditorFormatFloor, withPageFormatFloor } from '../../core/page-format.js';
45
45
 
46
46
  export { splitPage, composePage, pageRev } from './page-state.js';
47
47
 
@@ -55,7 +55,7 @@ export const LIVE_LIMITS = Object.freeze({
55
55
  movesBytes: 1024 * 1024,
56
56
  });
57
57
 
58
- const PAGE = /^page(\.[0-9]+)?$/;
58
+ const PAGE = /^blockeditor(\.[0-9]+)?$/;
59
59
  export const COMPACTOR = 'compactor';
60
60
  const isContainer = (v) => v !== null && typeof v === 'object';
61
61
 
@@ -239,7 +239,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
239
239
  lastSentAt: new Map(),
240
240
  seen: new Map(),
241
241
  emptySince: null,
242
- ...(inspectPage(html).kind === 'yjs' && { pageTransition: makePageTransition() }),
242
+ ...(inspectPage(html).kind === 'yjs' && { blockeditorTransition: makePageTransition() }),
243
243
  compaction: hostMode(inspectPage(html).kind === 'yjs'),
244
244
  });
245
245
 
@@ -249,7 +249,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
249
249
  return total;
250
250
  };
251
251
  const heldBy = (room) => {
252
- let total = (room.receiptBytes ?? 0) + (room.pageTransition?.bytes ?? 0) + (room.compaction?.job?.reserved ?? 0);
252
+ let total = (room.receiptBytes ?? 0) + (room.blockeditorTransition?.bytes ?? 0) + (room.compaction?.job?.reserved ?? 0);
253
253
  for (const s of room.stores.values()) total += s.bytes + s.logBytes;
254
254
  return total;
255
255
  };
@@ -265,7 +265,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
265
265
  return {
266
266
  limit,
267
267
  over() {
268
- if (answer.failed || (state.lastFull && state.deletesSince === 0)) return { error: 'page-full' };
268
+ if (answer.failed || (state.lastFull && state.deletesSince === 0)) return { error: 'blockeditor-full' };
269
269
  due(room, true);
270
270
  return { error: 'checkpoint-required', retryAfter: 0 };
271
271
  },
@@ -342,7 +342,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
342
342
 
343
343
  const pageSource = (key, nodeId) => {
344
344
  if (nodeId == null) throw new PageConversionError('invalid-document');
345
- if (pageReservations.has(key) || persistentReservations.has(key) || reservedNodes.has(String(nodeId))) throw new PageConversionError('page-converting');
345
+ if (pageReservations.has(key) || persistentReservations.has(key) || reservedNodes.has(String(nodeId))) throw new PageConversionError('blockeditor-converting');
346
346
  let room = rooms.get(key) ?? null;
347
347
  if (room && String(room.nodeId) !== String(nodeId)) {
348
348
  used -= heldBy(room);
@@ -355,7 +355,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
355
355
  const reservePageTarget = (key, nodeId, room, html, details) => {
356
356
  const receipts = new Set([...(room?.lastN.keys() ?? [])].filter((key) => PAGE.test(key.slice(0, key.indexOf('\n')))));
357
357
  if (receipts.size >= LIVE_LIMITS.receipts ||
358
- (room?.pageTransition?.receiptCount ?? 0) + receipts.size > LIVE_LIMITS.receipts) throw new PageConversionError('receipt-capacity');
358
+ (room?.blockeditorTransition?.receiptCount ?? 0) + receipts.size > LIVE_LIMITS.receipts) throw new PageConversionError('receipt-capacity');
359
359
  const names = details.names ?? [...new Set([
360
360
  ...[...(room?.stores.keys() ?? [])].filter((name) => PAGE.test(name)),
361
361
  ...[...receipts].map((key) => key.slice(0, key.indexOf('\n'))),
@@ -373,7 +373,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
373
373
  heldCount: others.length,
374
374
  });
375
375
  for (const store of stores.values()) store.from = null;
376
- budget = transition.bytes - (room.pageTransition?.historyBytes ?? 0) +
376
+ budget = transition.bytes - (room.blockeditorTransition?.historyBytes ?? 0) +
377
377
  [...stores.values()].reduce((sum, store) => sum + store.bytes, 0);
378
378
  const replacedBytes = [...room.stores].filter(([name]) => PAGE.test(name))
379
379
  .reduce((sum, [, store]) => sum + store.bytes + store.logBytes, 0);
@@ -413,11 +413,11 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
413
413
  }
414
414
  if (!isContainer(value) || Array.isArray(value) !== Array.isArray(store.value)) continue;
415
415
  if (PAGE.test(tag.name)) {
416
- if (room.pageTransition?.format === 'yjs') {
417
- if (tag.name !== 'page' || !isYPageValue(value)) continue;
416
+ if (room.blockeditorTransition?.format === 'yjs') {
417
+ if (tag.name !== 'blockeditor' || !isYPageValue(value)) continue;
418
418
  } else if (isYPageValue(value, { complete: false })) continue;
419
419
  }
420
- const host = Boolean(room.compaction && tag.name === 'page' && room.pageTransition?.format === 'yjs');
420
+ const host = Boolean(room.compaction && tag.name === 'blockeditor' && room.blockeditorTransition?.format === 'yjs');
421
421
  const before = store.landed;
422
422
  const base = baseOf(room, store, tag.at);
423
423
  // A capture (stamped, and still the value its stamp names) holds no merge
@@ -548,9 +548,9 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
548
548
  return reservePageTarget(key, nodeId, room, html, { source, base, epoch, names, format: 'yjs' });
549
549
  },
550
550
 
551
- reservePageInitial(key, { nodeId, currentHtml, html, ifMatch, pageWrite = null, createDocument }) {
551
+ reservePageInitial(key, { nodeId, currentHtml, html, ifMatch, blockeditorWrite = null, createDocument }) {
552
552
  const room = pageSource(key, nodeId);
553
- checkPageInitial({ currentHtml, html, ifMatch, room, pageWrite, createDocument });
553
+ checkPageInitial({ currentHtml, html, ifMatch, room, blockeditorWrite, createDocument });
554
554
  return reservePageTarget(key, nodeId, room, html, { source: 'initial', names: [], format: 'yjs' });
555
555
  },
556
556
 
@@ -591,7 +591,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
591
591
  room.stores.set(name, store);
592
592
  bytes += store.bytes;
593
593
  }
594
- room.pageTransition = transition;
594
+ room.blockeditorTransition = transition;
595
595
  if (hostLimits) room.compaction = hostMode(transition.format === 'yjs');
596
596
  reservation.target = null;
597
597
  used += heldBy(room) - before - reservation.budget;
@@ -662,8 +662,8 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
662
662
  throw new PersistentReservationError('invalid-target');
663
663
  }
664
664
  if (PAGE.test(name)) {
665
- if (record.room.pageTransition?.format === 'yjs') {
666
- if (name !== 'page' || !isYPageValue(value) || value.g !== store.value.g) {
665
+ if (record.room.blockeditorTransition?.format === 'yjs') {
666
+ if (name !== 'blockeditor' || !isYPageValue(value) || value.g !== store.value.g) {
667
667
  throw new PersistentReservationError('invalid-target');
668
668
  }
669
669
  } else if (isYPageValue(value, { complete: false })) throw new PersistentReservationError('invalid-target');
@@ -839,14 +839,14 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
839
839
  if (current === room && persistentReservations.has(room.key)) return { error: 'checkpointing' };
840
840
  if (!isContainer(frame)) return { error: 'bad-frame' };
841
841
  if (current !== room) return { stale: true, epoch: current?.epoch ?? null };
842
- if (pageReservations.has(room.key) && PAGE.test(frame.name)) return { error: 'page-converting' };
842
+ if (pageReservations.has(room.key) && PAGE.test(frame.name)) return { error: 'blockeditor-converting' };
843
843
  // Made for an earlier room: the tab resends its ops once it has this room's state.
844
844
  if ((PAGE.test(frame.name) || frame.epoch !== undefined) && frame.epoch !== room.epoch) return { stale: true, epoch: room.epoch };
845
- const transition = PAGE.test(frame.name) ? room.pageTransition : null;
846
- if (PAGE.test(frame.name) && (transition?.format === 'yjs' || frame.pageGeneration !== undefined) &&
847
- (frame.pageGeneration ?? null) !== (transition?.id ?? null)) return { error: 'page-generation' };
845
+ const transition = PAGE.test(frame.name) ? room.blockeditorTransition : null;
846
+ if (PAGE.test(frame.name) && (transition?.format === 'yjs' || frame.blockeditorGeneration !== undefined) &&
847
+ (frame.blockeditorGeneration ?? null) !== (transition?.id ?? null)) return { error: 'blockeditor-generation' };
848
848
  const converted = transition?.format === 'yjs';
849
- if (converted && frame.name !== 'page') return { error: 'page-format' };
849
+ if (converted && frame.name !== 'blockeditor') return { error: 'blockeditor-format' };
850
850
  const store = room.stores.get(frame.name);
851
851
  if (!store) return { error: 'unknown-store' };
852
852
  if (!Number.isInteger(frame.n) || frame.n < 1) return { error: 'bad-frame' };
@@ -862,11 +862,11 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
862
862
  return { error: 'bad-op', message: err.message };
863
863
  }
864
864
  if (ops.some((op) => op.path.length !== 2 || op.path[0] !== 'y' ||
865
- !(op.op === 'del' || (op.op === 'set' && typeof op.value === 'string')))) return { error: 'page-format' };
865
+ !(op.op === 'del' || (op.op === 'set' && typeof op.value === 'string')))) return { error: 'blockeditor-format' };
866
866
  }
867
867
  const nKey = frame.name + '\n' + clientId;
868
868
  const last = room.lastN.get(nKey);
869
- const admission = room.compaction && frame.name === 'page' && !receiptHas(last, frame.n) ? admit(room, store, frame, ops) : null;
869
+ const admission = room.compaction && frame.name === 'blockeditor' && !receiptHas(last, frame.n) ? admit(room, store, frame, ops) : null;
870
870
  if (admission?.error) return admission;
871
871
  // Answered with the seq the sender's last frame landed at, so the tab knows
872
872
  // which applied state holds it.
@@ -914,12 +914,12 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
914
914
  }
915
915
  if (converted && !isYPageValue(value, { complete: false })) {
916
916
  undo();
917
- return { error: 'page-format' };
917
+ return { error: 'blockeditor-format' };
918
918
  }
919
919
  if (!converted && PAGE.test(frame.name) && (isYPageValue(value, { complete: false }) ||
920
920
  (hybridSource && (Object.hasOwn(value, 'y') || Object.hasOwn(value, 'v'))))) {
921
921
  undo();
922
- return { error: 'page-conversion-required' };
922
+ return { error: 'blockeditor-conversion-required' };
923
923
  }
924
924
  if (bytes > LIVE_LIMITS.storeBytes || roomBytes(room) - store.bytes + bytes > LIVE_LIMITS.roomBytes) {
925
925
  undo();
@@ -957,7 +957,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
957
957
  if (admission && done.length) admission.admitted(bytes);
958
958
  const writerGone = room.writer && connected && !connected.includes(room.writer);
959
959
  if ((!room.writer || writerGone) && (!connected || connected.includes(clientId))) room.writer = clientId;
960
- return { seq: room.seq, epoch: room.epoch, name: frame.name, ops, writer: room.writer, checkpoint: pressed(store), receiptGeneration: room.receiptGeneration ?? 0, ...(transition && { pageGeneration: transition.id }) };
960
+ return { seq: room.seq, epoch: room.epoch, name: frame.name, ops, writer: room.writer, checkpoint: pressed(store), receiptGeneration: room.receiptGeneration ?? 0, ...(transition && { blockeditorGeneration: transition.id }) };
961
961
  },
962
962
 
963
963
  /** Everything a joining tab needs, with clientId's own receipt per store. Values are the room's own objects: serialize, never mutate. With since in this room's epoch, moves carries each store's log entries after since.seq (null where the log no longer reaches back that far). */
@@ -970,12 +970,12 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
970
970
  }
971
971
  const hidden = Boolean(legacy && (capable !== undefined ? capable !== hostLimits.version : clientId && legacy.has(clientId)));
972
972
  for (const [name, s] of room.stores) {
973
- if (hidden && name === 'page') continue;
973
+ if (hidden && name === 'blockeditor') continue;
974
974
  const seen = {};
975
975
  const mine = clientId ? room.lastN.get(name + '\n' + clientId) : null;
976
976
  if (mine) seen[clientId] = mine.n;
977
977
  stores[name] = { value: s.value, ephemeral: s.ephemeral, from: s.from, landed: s.landed, floor: s.floor, last: s.last, seen, ...(mine?.saved?.length && { saved: mine.saved }),
978
- ...(name === 'page' && room.compaction && { compaction: { v: hostLimits.version, loose: hostLimits.loose, activeBytes: hostLimits.activeBytes } }) };
978
+ ...(name === 'blockeditor' && room.compaction && { compaction: { v: hostLimits.version, loose: hostLimits.loose, activeBytes: hostLimits.activeBytes } }) };
979
979
  }
980
980
  // A client's own read (since is its { epoch, seq }, or null for a first
981
981
  // read) carries moves: those after its seq in its own epoch, else this
@@ -983,8 +983,8 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
983
983
  // heals other tabs already sent by their steps. A broadcast passes no since
984
984
  // and carries none.
985
985
  const provenance = { provenanceVersion: 2, receiptGeneration: room.receiptGeneration ?? 0 };
986
- if (room.pageTransition) {
987
- provenance.pageTransition = pageTransitionState(room.pageTransition, clientId);
986
+ if (room.blockeditorTransition) {
987
+ provenance.blockeditorTransition = pageTransitionState(room.blockeditorTransition, clientId);
988
988
  }
989
989
  if (since === undefined) return { epoch: room.epoch, seq: room.seq, writer: room.writer, stores, ...provenance };
990
990
  const from = since && since.epoch === room.epoch && Number.isInteger(since.seq) ? since.seq : 0;
@@ -1110,7 +1110,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
1110
1110
  if (now() < state.job.deadline) return null;
1111
1111
  endJob(state.job);
1112
1112
  }
1113
- const store = room.stores.get('page');
1113
+ const store = room.stores.get('blockeditor');
1114
1114
  if (!store || pageReservations.has(room.key) || persistentReservations.has(room.key)) return null;
1115
1115
  const plan = planCompaction(store.value.y, { overflow }, hostLimits);
1116
1116
  if (!plan) return null;
@@ -1118,7 +1118,7 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
1118
1118
  if (used + reserved > liveBytes) return null;
1119
1119
  state.fence += 1;
1120
1120
  const token = Object.freeze({ key: room.key, epoch: room.epoch, fence: state.fence });
1121
- const job = { token, room, store, epoch: room.epoch, generation: room.pageTransition?.id ?? null, kind: plan.kind,
1121
+ const job = { token, room, store, epoch: room.epoch, generation: room.blockeditorTransition?.id ?? null, kind: plan.kind,
1122
1122
  sources: new Map(plan.sources), physical: Object.keys(store.value.y), reserved, deadline: now() + hostLimits.leaseMs, deletesAtBegin: state.deletesSince };
1123
1123
  state.job = job;
1124
1124
  used += reserved;
@@ -1141,8 +1141,8 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
1141
1141
  jobs.delete(token);
1142
1142
  const { room, store } = job;
1143
1143
  if (!endJob(job)) return { refused: 'stale-token' };
1144
- if (rooms.get(room.key) !== room || room.epoch !== job.epoch || room.stores.get('page') !== store ||
1145
- (room.pageTransition?.id ?? null) !== job.generation) return { refused: 'stale-token' };
1144
+ if (rooms.get(room.key) !== room || room.epoch !== job.epoch || room.stores.get('blockeditor') !== store ||
1145
+ (room.blockeditorTransition?.id ?? null) !== job.generation) return { refused: 'stale-token' };
1146
1146
  const y = store.value.y;
1147
1147
  const retire = job.physical.filter((k) => {
1148
1148
  const part = PART_KEY.exec(k);
@@ -1196,8 +1196,8 @@ export function createRooms({ now = Date.now, newEpoch = randomUUID, liveBytes =
1196
1196
  state.deletesSince -= job.deletesAtBegin;
1197
1197
  }
1198
1198
  if (pageShape(store.value.y, hostLimits).loose >= hostLimits.soft || !pageShape(store.value.y, hostLimits).normal) due(room);
1199
- return { seq: room.seq, epoch: room.epoch, name: 'page', ops, from: COMPACTOR, kind: job.kind, landed: store.landed,
1200
- ...(room.pageTransition && { pageGeneration: room.pageTransition.id }) };
1199
+ return { seq: room.seq, epoch: room.epoch, name: 'blockeditor', ops, from: COMPACTOR, kind: job.kind, landed: store.landed,
1200
+ ...(room.blockeditorTransition && { blockeditorGeneration: room.blockeditorTransition.id }) };
1201
1201
  },
1202
1202
 
1203
1203
  /** Every room's key. */
@@ -3,10 +3,10 @@ import { cloneJson, serializeLive } from './live-data-ops.js';
3
3
 
4
4
  export function makePageTransition({ room = null, format = 'yjs', sources = [], receipts = new Map() } = {}) {
5
5
  const archived = new Map([...receipts].map(([key, receipt]) => [key, cloneJson(receipt)]));
6
- const history = [...(room?.pageTransition?.history ?? [])];
6
+ const history = [...(room?.blockeditorTransition?.history ?? [])];
7
7
  if (archived.size) history.push({
8
8
  epoch: room.epoch,
9
- generation: room.pageTransition?.id ?? null,
9
+ generation: room.blockeditorTransition?.id ?? null,
10
10
  receiptGeneration: room.receiptGeneration ?? 0,
11
11
  sources: [...sources],
12
12
  receipts: archived,
@@ -5,9 +5,9 @@ import { liveTags } from './live-data-tags.js';
5
5
  import { recent } from './recent.js';
6
6
  import { project } from '../../page/projection.js';
7
7
  import { blocksFromPageValues } from '../../page/ybind/decode.js';
8
- import { pageFormatDecision, pageFormatFloor, pageFormatRefusal, requiredPageFormat, withPageFormatFloor } from '../../page/format-level.js';
8
+ import { pageFormatDecision, blockeditorFormatFloor, pageFormatRefusal, requiredPageFormat, withPageFormatFloor } from '../../page/format-level.js';
9
9
 
10
- const PAGE = /^page(\.[0-9]+)?$/;
10
+ const PAGE = /^blockeditor(\.[0-9]+)?$/;
11
11
  const PART = /^(.+)\.(\d+)\.(\d+)$/;
12
12
  const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
13
13
  export const isHybridPageValue = (value) => record(value) && record(value.blocks) && (value.deleted == null || record(value.deleted)) && Object.keys(value).every((key) => key === 'blocks' || key === 'deleted');
@@ -37,6 +37,34 @@ export function isYPageValue(value, { complete = true } = {}) {
37
37
  return true;
38
38
  }
39
39
 
40
+ // A live tag in the format before 1.24.0 (`merge="page"` or `page.N`) holding a Yjs envelope.
41
+ // Hosts refuse such a write, because 1.24.0 reads none of it. An author's own live store
42
+ // named `page` with ordinary JSON is not the block editor's and is never refused.
43
+ const OLD_TAG = /^page(\.[0-9]+)?$/;
44
+ const OLD_FORMAT_MSG = 'This document now uses the new Block editor format, so this change was not saved. Copy any text you need from this tab, then reload.';
45
+
46
+ // Thrown by a host's save path; hosts answer it like a format refusal: 409, code and message.
47
+ export function oldFormatRefusal() {
48
+ const error = new Error(OLD_FORMAT_MSG);
49
+ error.name = 'BlockeditorOldFormat';
50
+ error.code = 'blockeditor-old-format';
51
+ error.statusCode = 409;
52
+ return error;
53
+ }
54
+
55
+ export function oldBlockeditorFormat(html) {
56
+ if (typeof html !== 'string' || !/merge/i.test(html)) return false;
57
+ return liveTags(html, { includeDuplicates: true }).some((tag) => {
58
+ if (!OLD_TAG.test(tag.name)) return false;
59
+ try {
60
+ const value = parseJsonRelaxed(tag.text);
61
+ return record(value) && (Object.hasOwn(value, 'y') || Object.hasOwn(value, 'v'));
62
+ } catch {
63
+ return false;
64
+ }
65
+ });
66
+ }
67
+
40
68
  // Callers get their own copy of the tags, so none can change another's answer.
41
69
  export function inspectPage(html) {
42
70
  const answer = inspected(html);
@@ -60,7 +88,7 @@ const inspected = recent(4, (html) => {
60
88
  }
61
89
  }
62
90
  if (tags.some(([, value]) => record(value) && (Object.hasOwn(value, 'y') || Object.hasOwn(value, 'v')))) {
63
- if (tags.length !== 1 || tags[0][0] !== 'page' || !isYPageValue(tags[0][1])) return { kind: 'invalid', reason: 'invalid-yjs', tags };
91
+ if (tags.length !== 1 || tags[0][0] !== 'blockeditor' || !isYPageValue(tags[0][1])) return { kind: 'invalid', reason: 'invalid-yjs', tags };
64
92
  return { kind: 'yjs', tags };
65
93
  }
66
94
  return { kind: tags.every(([, value]) => isHybridPageValue(value)) ? 'hybrid' : 'other', tags };
@@ -94,16 +122,16 @@ export function captureFormat(html) {
94
122
  // The floor decision for one page write, taken before anything is mutated.
95
123
  // `floor` is the document's established floor, `required` what the captured
96
124
  // content needs (its own marker included) and `capability` what the writer
97
- // declares in X-Clay-Page-Format. `admitted` lists the capabilities of the page
125
+ // declares in X-Clay-Blockeditor-Format. `admitted` lists the capabilities of the page
98
126
  // editors the room has admitted, so a floor rise is refused while an editor that
99
127
  // could not read the result is connected. On success the returned body carries
100
128
  // the marker the room must persist, so a stale save that omitted it cannot lower
101
129
  // the floor.
102
130
  export function checkPageFormatAdmission({ currentHtml, html, capability = 0, admitted = [] }) {
103
- const established = pageFormatFloor(currentHtml ?? '');
131
+ const established = blockeditorFormatFloor(currentHtml ?? '');
104
132
  const decision = pageFormatDecision({
105
133
  floor: established,
106
- required: Math.max(captureFormat(html), pageFormatFloor(html ?? '')),
134
+ required: Math.max(captureFormat(html), blockeditorFormatFloor(html ?? '')),
107
135
  capability,
108
136
  admitted,
109
137
  });
@@ -1,7 +1,7 @@
1
1
  import { ifMatchSatisfied } from './spec-wire.js';
2
2
  import { inspectPage, pageBaseline } from './page-admission.js';
3
3
 
4
- const PAGE = /^page(\.[0-9]+)?$/;
4
+ const PAGE = /^blockeditor(\.[0-9]+)?$/;
5
5
  const BASE = /^[0-9a-f]{64}$/;
6
6
  const EPOCH = /^[A-Za-z0-9-]{1,64}$/;
7
7