@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
@@ -11,7 +11,7 @@ const lineages = new Map();
11
11
 
12
12
  function storedArticle(html, doc) {
13
13
  const $ = load(html);
14
- const nodes = $('article[data-page]').toArray().filter((node) => !inert(node));
14
+ const nodes = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
15
15
  if (nodes.length !== 1) return null;
16
16
  const article = doc.createElement('article');
17
17
  for (const [name, value] of Object.entries(nodes[0].attribs)) article.setAttribute(name, value);
@@ -31,7 +31,7 @@ function storedLineage(html, createDocument) {
31
31
  return lineage;
32
32
  }
33
33
 
34
- export function checkInitialLineage({ currentHtml, html, pageWrite = null, createDocument }) {
34
+ export function checkInitialLineage({ currentHtml, html, blockeditorWrite = null, createDocument }) {
35
35
  const lineage = storedLineage(currentHtml, createDocument);
36
36
  if (!lineage) return;
37
37
  let doc;
@@ -42,7 +42,7 @@ export function checkInitialLineage({ currentHtml, html, pageWrite = null, creat
42
42
  }
43
43
  try {
44
44
  if (lineage.client !== null && !Y.decodeStateVector(Y.encodeStateVector(doc)).has(lineage.client)) refuse('seed-lineage');
45
- if (typeof pageWrite === 'string' && pageWrite.trim() === 'v1' && seedOnly(doc, lineage.client) &&
45
+ if (typeof blockeditorWrite === 'string' && blockeditorWrite.trim() === 'v1' && seedOnly(doc, lineage.client) &&
46
46
  splitPage(html).file === currentHtml) refuse('nothing-to-write');
47
47
  } finally {
48
48
  doc.destroy();
@@ -51,6 +51,6 @@ export function checkInitialLineage({ currentHtml, html, pageWrite = null, creat
51
51
 
52
52
  export function checkOrdinaryLineage({ currentHtml, html, room = null }) {
53
53
  const before = inspectPage(currentHtml ?? '');
54
- const stored = before.kind === 'yjs' ? before.tags[0][1] : room?.stores.get('page')?.value ?? null;
54
+ const stored = before.kind === 'yjs' ? before.tags[0][1] : room?.stores.get('blockeditor')?.value ?? null;
55
55
  if (stored && !sharesLineage(stored, inspectPage(html).tags[0][1])) refuse('seed-lineage');
56
56
  }
@@ -5,13 +5,13 @@ import { project, parseModel, renderModel, fragmentCleaner } from '../../page/pr
5
5
  import { blocksFromPageValues } from '../../page/ybind/decode.js';
6
6
  import { mergePageModels } from '../../page/merge.js';
7
7
  import { inert, pageRev } from './page-state.js';
8
- import { pageFormatFloor, withPageFormatFloor } from '../../page/format-level.js';
8
+ import { blockeditorFormatFloor, withPageFormatFloor } from '../../page/format-level.js';
9
9
 
10
10
  // The lone article of html: its element rebuilt in doc, and where its start tag
11
11
  // and its contents sit in html.
12
12
  export function articleOf(html, doc) {
13
13
  const $ = load(html, { sourceCodeLocationInfo: true });
14
- const nodes = $('article[data-page]').toArray().filter((node) => !inert(node));
14
+ const nodes = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
15
15
  if (nodes.length !== 1) return null;
16
16
  const loc = nodes[0].sourceCodeLocation;
17
17
  if (!loc?.startTag || !loc?.endTag) return null;
@@ -70,7 +70,7 @@ export function rebasePage({ storedHtml, state, incomingHtml, baseHtml, createDo
70
70
  const body = incomingHtml.slice(0, incoming.startTag.startOffset) + open + inner + incomingHtml.slice(innerEnd);
71
71
  // The floor is canonical metadata outside the article, and it only rises: a
72
72
  // writer that rebased from a version before activation cannot lower it.
73
- const html = withPageFormatFloor(body, Math.max(pageFormatFloor(storedHtml), pageFormatFloor(incomingHtml)));
73
+ const html = withPageFormatFloor(body, Math.max(blockeditorFormatFloor(storedHtml), blockeditorFormatFloor(incomingHtml)));
74
74
  return { html, conflicts: merged.conflicts };
75
75
  }
76
76
 
@@ -106,7 +106,7 @@ export function closestBase(candidates, incomingHtml, createDocument) {
106
106
  export function stampPageRev(html, rev, floor = 0) {
107
107
  if (typeof html !== 'string' || !rev) return null;
108
108
  const $ = load(html, { sourceCodeLocationInfo: true });
109
- const nodes = $('article[data-page]').toArray().filter((node) => !inert(node));
109
+ const nodes = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
110
110
  const loc = nodes.length === 1 ? nodes[0].sourceCodeLocation?.startTag : null;
111
111
  if (!loc) return null;
112
112
  const stamped = stampOpen(html, loc, rev);
@@ -6,7 +6,7 @@ import { entriesOf } from '../../page/ybind/parts.js';
6
6
  import { rangesOf, within, docRanges } from '../../page/ybind/containment.js';
7
7
 
8
8
  /**
9
- * The D13 page-room conformance fixture: a seeded run of several tabs, restarts,
9
+ * The D13 blockeditor-room conformance fixture: a seeded run of several tabs, restarts,
10
10
  * resets and legacy file writes over one page room, driven through an adapter
11
11
  * that stands in for a host's wiring of the room. Every run throws with the seed
12
12
  * and the invariant it broke. The module reads no files: the caller passes the
@@ -39,14 +39,14 @@ import { rangesOf, within, docRanges } from '../../page/ybind/containment.js';
39
39
  *
40
40
  * readState({ key, tabId, capable })
41
41
  * The JSON the client receives for a state read: { epoch, seq, writer,
42
- * stores: { page: { value, ... }, ... }, ... }, with the page store's value
42
+ * stores: { blockeditor: { value, ... }, ... }, ... }, with the page store's value
43
43
  * as the client's re-read carries it. `tabId` is the reading client (null for
44
44
  * a broadcast's read) and `capable` says whether it advertises the page
45
45
  * compaction capability (default true). Answers { status: 404, body: null }
46
46
  * when the host has no room for key.
47
47
  *
48
48
  * apply({ key, tabId, frame })
49
- * Apply one frame { name, n, epoch, pageGeneration, compaction, ops } from
49
+ * Apply one frame { name, n, epoch, blockeditorGeneration, compaction, ops } from
50
50
  * tabId, atomically. Answer { status: 200, body: { duplicate, seq, epoch, ... } }
51
51
  * when the host took it, { status, body: { error, ... } } with the host's
52
52
  * status for a refusal (the error code and any retryAfter), or
@@ -82,7 +82,7 @@ import { rangesOf, within, docRanges } from '../../page/ybind/containment.js';
82
82
  */
83
83
 
84
84
  const NET = 'net';
85
- const TAG = /merge="page" clay="live"(?: live-at="[^"]*")?>(.*?)<\/script>/;
85
+ const TAG = /merge="blockeditor" clay="live"(?: live-at="[^"]*")?>(.*?)<\/script>/;
86
86
 
87
87
  function rng(seed) {
88
88
  let s = seed >>> 0 || 1;
@@ -167,7 +167,7 @@ export function createRoomsAdapter({ now = null, newEpoch = null, compaction = n
167
167
  body: {
168
168
  reported: rooms.used(),
169
169
  receiptBytes: target.receiptBytes ?? 0,
170
- transitionBytes: target.pageTransition?.bytes ?? 0,
170
+ transitionBytes: target.blockeditorTransition?.bytes ?? 0,
171
171
  reservedBytes: target.compaction?.job?.reserved ?? 0,
172
172
  stores,
173
173
  },
@@ -206,7 +206,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
206
206
  const r = rng(seed);
207
207
  const pick = (n) => Math.floor(r() * n);
208
208
  const limits = { activeBytes, loose: adapter.limits.loose, soft: adapter.limits.soft, leaseMs: adapter.limits.leaseMs, tailFloor };
209
- const htmlOf = (value, at = null) => template.replace(TAG, () => `merge="page" clay="live"${at ? ` live-at="${at}"` : ''}>${JSON.stringify(value)}</script>`);
209
+ const htmlOf = (value, at = null) => template.replace(TAG, () => `merge="blockeditor" clay="live"${at ? ` live-at="${at}"` : ''}>${JSON.stringify(value)}</script>`);
210
210
  const fail = (message, extra = {}) => { throw Object.assign(new Error(`seed ${seed}: ${message}`), extra); };
211
211
  const dueSet = new Set();
212
212
  adapter.onCompactionDue(({ key, overflow }) => { if (key === KEY) dueSet.add({ key, overflow }); });
@@ -218,7 +218,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
218
218
  if (answer.status !== 200) fail(`state read refused with ${answer.status}`);
219
219
  return answer.body;
220
220
  };
221
- const page = () => state().stores.page.value;
221
+ const page = () => state().stores.blockeditor.value;
222
222
  const usage = () => {
223
223
  const answer = adapter.usage({ key: KEY });
224
224
  if (answer.status !== 200) fail(`usage read refused with ${answer.status}`);
@@ -246,7 +246,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
246
246
 
247
247
  const check = (where) => {
248
248
  const u = usage();
249
- const bytes = u.stores.page.bytes;
249
+ const bytes = u.stores.blockeditor.bytes;
250
250
  const shape = pageShape(page().y, limits);
251
251
  stats.maxLoose = Math.max(stats.maxLoose, shape.loose);
252
252
  stats.maxBytes = Math.max(stats.maxBytes, bytes);
@@ -291,12 +291,12 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
291
291
  const out = adapter.fileWritten({ key: KEY, html: htmlOf(file, at) });
292
292
  legacy.writes++;
293
293
  const after = state();
294
- if (!after.stores.page) fail('a merge dropped the page store');
295
- const y = after.stores.page.value.y;
294
+ if (!after.stores.blockeditor) fail('a merge dropped the page store');
295
+ const y = after.stores.blockeditor.value.y;
296
296
  if (keysBefore.some((k) => !(k in y) || y[k] !== valueBefore.y[k])) fail('a file merge deleted or changed a room key');
297
297
  if (!contains([update])) fail('a file merge lost the legacy edit');
298
298
  if (out.body?.valueChanged) legacy.merged++;
299
- if (out.body?.landed?.includes('page')) stats.landed++;
299
+ if (out.body?.landed?.includes('blockeditor')) stats.landed++;
300
300
  admitted.push(update);
301
301
  const u = usage();
302
302
  if (u.reported !== held(u)) fail('budget after merge');
@@ -330,13 +330,13 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
330
330
  const before = serializeLive(page());
331
331
  const seqBefore = state().seq;
332
332
  const now = state();
333
- const answer = adapter.apply({ key: KEY, tabId: t.id, frame: { name: 'page', n: f.n, epoch: now.epoch, pageGeneration: now.pageTransition?.id ?? null, compaction: 1, ops: [{ op: 'set', path: ['y', f.key], value: f.value }] } });
333
+ const answer = adapter.apply({ key: KEY, tabId: t.id, frame: { name: 'blockeditor', n: f.n, epoch: now.epoch, blockeditorGeneration: now.blockeditorTransition?.id ?? null, compaction: 1, ops: [{ op: 'set', path: ['y', f.key], value: f.value }] } });
334
334
  const body = answer.body ?? {};
335
335
  if (body.error) {
336
336
  if (serializeLive(page()) !== before || state().seq !== seqBefore) fail(`refusal ${body.error} changed the room`);
337
337
  stats.unchangedRefusals++;
338
338
  if (body.error === 'checkpoint-required') { stats.refusedRetry++; return; }
339
- if (body.error === 'page-full') { stats.capacity++; t.blocked = true; return; }
339
+ if (body.error === 'blockeditor-full') { stats.capacity++; t.blocked = true; return; }
340
340
  fail(`unexpected refusal ${body.error}`);
341
341
  }
342
342
  if (body.stale) { t.inflight = null; return; }
@@ -455,7 +455,7 @@ export function runPageRoomConformance({ adapter, template, seed = 1, steps = 40
455
455
  roomDoc.destroy();
456
456
  }
457
457
  const end = page();
458
- const out = { seed, ...stats, legacyWrites: legacy.writes, legacyMerged: legacy.merged, blocked: blocked.length, end: { bytes: usage().stores.page.bytes, ...pageShape(end.y, limits) } };
458
+ const out = { seed, ...stats, legacyWrites: legacy.writes, legacyMerged: legacy.merged, blocked: blocked.length, end: { bytes: usage().stores.blockeditor.bytes, ...pageShape(end.y, limits) } };
459
459
  if (log) console.log(JSON.stringify(out));
460
460
  return out;
461
461
  }
@@ -15,8 +15,8 @@ const span = (node) => {
15
15
  // Offsets only, never the parsed tree, so the remembered answers stay small.
16
16
  const locate = recent(4, (html) => {
17
17
  const $ = load(html, { sourceCodeLocationInfo: true });
18
- const articles = $('article[data-page]').toArray().filter((node) => !inert(node));
19
- const tags = $('script[merge="page"]').toArray().filter((node) => !inert(node) &&
18
+ const articles = $('article[data-blockeditor]').toArray().filter((node) => !inert(node));
19
+ const tags = $('script[merge="blockeditor"]').toArray().filter((node) => !inert(node) &&
20
20
  ($(node).attr('clay') || '').split(/\s+/).includes('live'));
21
21
  return Object.freeze({
22
22
  articles: articles.length,
@@ -6,7 +6,7 @@ import { PageFormatError } from './page-write-guard.js';
6
6
  import { ifMatchSatisfied } from './spec-wire.js';
7
7
  import { checkInitialLineage, checkOrdinaryLineage } from './page-lineage.js';
8
8
 
9
- const PAGE = /^page(\.[0-9]+)?$/;
9
+ const PAGE = /^blockeditor(\.[0-9]+)?$/;
10
10
  const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
11
11
  const refuse = (reason) => { throw new PageConversionError(reason); };
12
12
 
@@ -26,7 +26,7 @@ function claimsYPage(html) {
26
26
  function checkInitialRoom(room) {
27
27
  if ([...(room?.stores.keys() ?? [])].some((name) => PAGE.test(name)) ||
28
28
  [...(room?.lastN.keys() ?? [])].some((key) => PAGE.test(key.slice(0, key.indexOf('\n')))) ||
29
- (room?.pageTransition && room.pageTransition.format !== 'none')) refuse('room-page-authority');
29
+ (room?.blockeditorTransition && room.blockeditorTransition.format !== 'none')) refuse('room-blockeditor-authority');
30
30
  }
31
31
 
32
32
  function checkInitialCondition(ifMatch, currentHtml) {
@@ -34,18 +34,18 @@ function checkInitialCondition(ifMatch, currentHtml) {
34
34
  !ifMatchSatisfied(ifMatch, currentHtml)) refuse('file-changed');
35
35
  }
36
36
 
37
- export function checkPageInitial({ currentHtml, html, ifMatch, room = null, pageWrite = null, createDocument }) {
37
+ export function checkPageInitial({ currentHtml, html, ifMatch, room = null, blockeditorWrite = null, createDocument }) {
38
38
  if (typeof currentHtml !== 'string' || inspectPage(currentHtml).kind !== 'none') refuse('invalid-source');
39
39
  if (typeof html !== 'string' || inspectPage(html).kind !== 'yjs') refuse('invalid-capture');
40
40
  checkInitialRoom(room);
41
41
  checkInitialCondition(ifMatch, currentHtml);
42
- checkInitialLineage({ currentHtml, html, pageWrite, createDocument });
42
+ checkInitialLineage({ currentHtml, html, blockeditorWrite, createDocument });
43
43
  }
44
44
 
45
- export function checkPageWriteAdmission({ currentHtml, html, header = null, ifMatch, room = null, pageWrite = null, createDocument }) {
45
+ export function checkPageWriteAdmission({ currentHtml, html, header = null, ifMatch, room = null, blockeditorWrite = null, createDocument }) {
46
46
  const before = inspectPage(currentHtml ?? '');
47
47
  const after = inspectPage(html ?? '');
48
- const converted = before.kind === 'yjs' || room?.pageTransition?.format === 'yjs';
48
+ const converted = before.kind === 'yjs' || room?.blockeditorTransition?.format === 'yjs';
49
49
  if (converted && after.kind === 'none' && header == null) return 'ordinary';
50
50
  if (converted && after.kind !== 'yjs') throw new PageFormatError();
51
51
  if (header !== null && header !== undefined) {
@@ -64,6 +64,6 @@ export function checkPageWriteAdmission({ currentHtml, html, header = null, ifMa
64
64
  if (before.kind !== 'none' || typeof currentHtml !== 'string') refuse('invalid-source');
65
65
  checkInitialRoom(room);
66
66
  checkInitialCondition(ifMatch, currentHtml);
67
- checkInitialLineage({ currentHtml, html, pageWrite, createDocument });
67
+ checkInitialLineage({ currentHtml, html, blockeditorWrite, createDocument });
68
68
  return 'initial';
69
69
  }
@@ -4,13 +4,13 @@ export class PageFormatError extends Error {
4
4
  constructor() {
5
5
  super('A converted page must retain one complete Yjs page value');
6
6
  this.name = 'PageFormatError';
7
- this.code = 'page-format';
7
+ this.code = 'blockeditor-format';
8
8
  this.statusCode = 409;
9
9
  }
10
10
  }
11
11
 
12
12
  export function checkPageWriteFormat({ currentHtml, html, room = null }) {
13
- if (room?.pageTransition?.format !== 'yjs' && inspectPage(currentHtml ?? '').kind !== 'yjs') return;
13
+ if (room?.blockeditorTransition?.format !== 'yjs' && inspectPage(currentHtml ?? '').kind !== 'yjs') return;
14
14
  const kind = inspectPage(html ?? '').kind;
15
15
  if (kind !== 'yjs' && kind !== 'none') throw new PageFormatError();
16
16
  }
@@ -19,5 +19,5 @@ export function checkPageWriteFormat({ currentHtml, html, room = null }) {
19
19
  // outside edit or restore replaced the article, so the room's unsaved page
20
20
  // edits belong in a recovered copy and the room must start over.
21
21
  export function pageLineageLost(room, html) {
22
- return room?.pageTransition?.format === 'yjs' && room.stores.has('page') && inspectPage(html ?? '').kind === 'none';
22
+ return room?.blockeditorTransition?.format === 'yjs' && room.stores.has('blockeditor') && inspectPage(html ?? '').kind === 'none';
23
23
  }
@@ -0,0 +1,265 @@
1
+ import { saveToken } from "../core/host-attrs.js";
2
+ import { storage } from "../lib/doc-storage.js";
3
+
4
+ const TITLE = "Saved data was overwritten";
5
+ const DONE_KEY = "data-guard-done";
6
+ const READ_TIMEOUT_MS = 6000;
7
+ const AFTER_SAVE_MS = 1500;
8
+ const MIN_READ_GAP_MS = 5000;
9
+ const VISIBLE_GAP_MS = 30000;
10
+ const UNREACHED = "Could not reach the server. Try again.";
11
+ const REFUSED = "That did not go through. Try again.";
12
+ const SAVE_FIRST = "Save your changes first, then try again.";
13
+ const RELOAD_FIRST = "Reload the page, then try again.";
14
+
15
+ let session = null;
16
+ let ui = null;
17
+
18
+ const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
19
+ const isPending = (event) => Boolean(event) && typeof event.id === "string" && event.status === "pending";
20
+ const allowed = (current, event, choice, flag) => flag === true && !current.refused.has(`${event.id}:${choice}`);
21
+ const canRestore = (current, event) => allowed(current, event, "restore", event.restorable);
22
+ const canRevert = (current, event) => allowed(current, event, "revert", event.canRevert);
23
+ const credentials = () => (saveToken() ? "omit" : "same-origin");
24
+ const headers = (extra = {}) => ({ "Document-URL": location.href, ...extra });
25
+
26
+ function removeServerBar() {
27
+ for (const bar of document.querySelectorAll("[data-htmlclay-guard]")) bar.remove();
28
+ }
29
+
30
+ function say(message, type) {
31
+ import("../ui/toast.js").then((module) => {
32
+ ui = module;
33
+ module.default(message, type);
34
+ }, () => {});
35
+ }
36
+
37
+ function describe(current, event) {
38
+ const summary = event.lossSummary || {};
39
+ const source = summary.provenance === "external" ? "A change from outside this page" : "A save from this page";
40
+ const loss = summary.removedItems > 0
41
+ ? `removed ${plural(summary.removedItems, "item")}`
42
+ : summary.destroyedAtoms > 0 ? `erased ${plural(summary.destroyedAtoms, "value")}` : "changed saved data";
43
+ const row = Array.isArray(event.preview) ? event.preview[0] : null;
44
+ const parts = [`${source} ${loss}.`];
45
+ if (row) parts.push(`${row.key}: ${row.yours} before, ${row.now} now.`);
46
+ if (canRestore(current, event)) {
47
+ if (event.droppedAdditions > 0) parts.push(`Restoring removes ${plural(event.droppedAdditions, "item")} added or changed since.`);
48
+ } else if (canRevert(current, event)) {
49
+ parts.push("This data no longer fits the page, but the whole change can be undone.");
50
+ } else {
51
+ parts.push("It cannot be restored from here.");
52
+ }
53
+ if (canRevert(current, event)) parts.push("Undo the change also removes edits made since.");
54
+ return parts.join(" ");
55
+ }
56
+
57
+ function actionsFor(current, event) {
58
+ const actions = [];
59
+ if (canRestore(current, event)) actions.push({ label: "Restore", onClick: () => choose(current, event, "restore") });
60
+ if (canRevert(current, event)) actions.push({ label: "Undo the change", onClick: () => choose(current, event, "revert") });
61
+ actions.push({ label: "Keep as is", onClick: () => choose(current, event, "dismiss") });
62
+ return actions;
63
+ }
64
+
65
+ async function draw(current, event) {
66
+ const key = [event.id, canRestore(current, event), canRevert(current, event), event.lastWriteAt, describe(current, event)].join("|");
67
+ if (current.drawn === key) return true;
68
+ current.drawn = key;
69
+ current.eventId = event.id;
70
+ try {
71
+ ui ??= await import("../ui/toast.js");
72
+ } catch {
73
+ if (current.drawn === key) {
74
+ current.drawn = null;
75
+ current.eventId = null;
76
+ }
77
+ return false;
78
+ }
79
+ if (session !== current || current.drawn !== key) return false;
80
+ ui.dismissPersistent(TITLE, { now: true });
81
+ ui.toastPersistent(TITLE, "warning", { detail: describe(current, event), actions: actionsFor(current, event) });
82
+ return true;
83
+ }
84
+
85
+ function clearNotice(current) {
86
+ current.drawn = null;
87
+ current.eventId = null;
88
+ ui?.dismissPersistent(TITLE, { now: true });
89
+ }
90
+
91
+ async function read(current) {
92
+ const turn = ++current.turn;
93
+ current.lastRead = Date.now();
94
+ const controller = new AbortController();
95
+ const timer = setTimeout(() => controller.abort(), READ_TIMEOUT_MS);
96
+ let body;
97
+ try {
98
+ const res = await fetch(current.url, {
99
+ method: "GET",
100
+ credentials: credentials(),
101
+ headers: headers(),
102
+ cache: "no-store",
103
+ redirect: "manual",
104
+ signal: controller.signal,
105
+ });
106
+ if (!res.ok) return;
107
+ body = await res.json();
108
+ } catch {
109
+ return;
110
+ } finally {
111
+ clearTimeout(timer);
112
+ }
113
+ if (session !== current || turn !== current.turn) return;
114
+ if (!body || typeof body !== "object" || !("event" in body)) return;
115
+ if (isPending(body.event)) {
116
+ if (current.resolved.has(body.event.id)) return;
117
+ if (await draw(current, body.event)) removeServerBar();
118
+ return;
119
+ }
120
+ clearNotice(current);
121
+ removeServerBar();
122
+ }
123
+
124
+ function forget(current, event) {
125
+ current.resolved.add(event.id);
126
+ if (current.eventId === event.id) clearNotice(current);
127
+ }
128
+
129
+ function finish(current, event, choice) {
130
+ forget(current, event);
131
+ if (choice === "dismiss") {
132
+ say("Kept the page as it is.", "info");
133
+ return;
134
+ }
135
+ storage.session.set(DONE_KEY, choice);
136
+ current.reload();
137
+ }
138
+
139
+ function settle(current, event, choice, status, body) {
140
+ if (session !== current) return;
141
+ if (status === 200 && body?.ok === true) {
142
+ finish(current, event, choice);
143
+ } else if (status === 404) {
144
+ forget(current, event);
145
+ say("This was already taken care of somewhere else. Reload to see the current page.", "info");
146
+ } else if (status === 409) {
147
+ current.drawn = null;
148
+ say("This page changed after the notice appeared. Check the notice and try again.", "warning");
149
+ read(current);
150
+ } else if (status === 422) {
151
+ current.refused.add(`${event.id}:${choice}`);
152
+ if (current.eventId === event.id && !current.resolved.has(event.id)) draw(current, event);
153
+ say(choice === "restore" ? "That data no longer fits this page, so it could not be restored." : "That earlier version is no longer available.", "warning");
154
+ } else if (status === 401 || status === 403) {
155
+ say(RELOAD_FIRST, "error");
156
+ } else {
157
+ say(status === 400 ? REFUSED : UNREACHED, "error");
158
+ }
159
+ }
160
+
161
+ async function saveFirst() {
162
+ const clay = window.clay;
163
+ if (!clay?.hasUnsavedChanges?.()) return true;
164
+ if (typeof clay.save?.flush !== "function") return false;
165
+ try {
166
+ await clay.save.flush();
167
+ } catch {
168
+ return false;
169
+ }
170
+ return !clay.hasUnsavedChanges();
171
+ }
172
+
173
+ async function choose(current, event, choice) {
174
+ if (current.busy || session !== current) return;
175
+ current.busy = true;
176
+ try {
177
+ if (choice !== "dismiss" && !(await saveFirst())) {
178
+ if (session === current) say(SAVE_FIRST, "warning");
179
+ return;
180
+ }
181
+ if (session !== current) return;
182
+ const res = await fetch(current.url, {
183
+ method: "POST",
184
+ credentials: credentials(),
185
+ headers: headers({ "Content-Type": "application/json" }),
186
+ redirect: "manual",
187
+ body: JSON.stringify({ id: event.id, choice }),
188
+ });
189
+ const body = await res.json().catch(() => null);
190
+ settle(current, event, choice, res.status, body);
191
+ } catch {
192
+ if (session === current) say(UNREACHED, "error");
193
+ } finally {
194
+ current.busy = false;
195
+ }
196
+ }
197
+
198
+ function onNotification(current, e) {
199
+ const { msgType, action, data } = e.detail || {};
200
+ if (msgType !== "data-loss") return;
201
+ if (action === "resolved") {
202
+ if (typeof data?.id !== "string") return;
203
+ current.turn++;
204
+ current.resolved.add(data.id);
205
+ if (current.eventId === data.id) clearNotice(current);
206
+ return;
207
+ }
208
+ if (action === "raised" && isPending(data) && !current.resolved.has(data.id)) {
209
+ current.turn++;
210
+ draw(current, data).then((drawn) => { if (drawn) removeServerBar(); }, () => {});
211
+ }
212
+ }
213
+
214
+ const readAt = (current) => Math.max(current.lastSave + AFTER_SAVE_MS, current.lastRead + MIN_READ_GAP_MS);
215
+
216
+ function armRead(current) {
217
+ if (current.timer) return;
218
+ current.timer = setTimeout(() => {
219
+ current.timer = null;
220
+ if (session !== current) return;
221
+ if (readAt(current) > Date.now()) armRead(current);
222
+ else read(current);
223
+ }, Math.max(readAt(current) - Date.now(), 0));
224
+ }
225
+
226
+ export function start({ href, reload = () => window.location.reload() }) {
227
+ if (session) return;
228
+ let url;
229
+ try {
230
+ url = new URL(href, location.href);
231
+ } catch {
232
+ return;
233
+ }
234
+ if (url.origin !== location.origin) return;
235
+ const listeners = new AbortController();
236
+ const current = {
237
+ url: url.href, reload, listeners, turn: 0, lastRead: 0, lastSave: 0, timer: null,
238
+ busy: false, drawn: null, eventId: null, resolved: new Set(), refused: new Set(),
239
+ };
240
+ session = current;
241
+ const { signal } = listeners;
242
+ document.addEventListener("clay:notification", (e) => onNotification(current, e), { signal });
243
+ document.addEventListener("clay:save-saved", () => {
244
+ current.lastSave = Date.now();
245
+ armRead(current);
246
+ }, { signal });
247
+ document.addEventListener("visibilitychange", () => {
248
+ if (document.visibilityState === "visible" && Date.now() - current.lastRead >= VISIBLE_GAP_MS) read(current);
249
+ }, { signal });
250
+ const done = storage.session.get(DONE_KEY);
251
+ if (done) {
252
+ storage.session.remove(DONE_KEY);
253
+ say(done === "restore" ? "Your saved data is back." : "The earlier version is back.", "success");
254
+ }
255
+ read(current);
256
+ }
257
+
258
+ export function stop() {
259
+ const current = session;
260
+ if (!current) return;
261
+ session = null;
262
+ current.listeners.abort();
263
+ clearTimeout(current.timer);
264
+ clearNotice(current);
265
+ }
@@ -48,13 +48,13 @@ import { mergeIgnoresAttr, mergeVetoesAttr } from '../lib/root-attrs.js';
48
48
  import { autosaveActive } from '../lib/autosave-state.js';
49
49
  import { hostSupports } from '../core/host-meta.js';
50
50
  import { PAGE_FORMAT, PAGE_FORMAT_HEADER } from '../core/page-format-wire.js';
51
- import { knownPageFormatFloor, pageFormatFloor, withPageFormatFloor } from '../core/page-format.js';
51
+ import { knownPageFormatFloor, blockeditorFormatFloor, withPageFormatFloor } from '../core/page-format.js';
52
52
  import { onSaveAccepted } from '../core/save-accepted.js';
53
53
  import { servedDocumentEtag } from '../core/host-attrs.js';
54
54
  import { recordEtag, seedEtag, lastSeenEtag, conditionalSaves, representedEtag, forgetRepresentedEtag } from '../core/etag.js';
55
55
  import { pageMaybeDirty, pauseGate, resumeGate, gateCaptureToken, gateClearIfUnchanged, gateMarkDirty } from '../lib/dirty-gate.js';
56
56
  import { gestureSeen } from '../lib/user-gesture.js';
57
- import { SyncStream } from './stream.js';
57
+ import { SyncStream, pageHidden } from './stream.js';
58
58
  import { streamOpened, streamClosed, streamEvent, LIVE_EVENTS } from '../live/stream-bridge.js';
59
59
  import { conflicts, conflictLedger, loadConflicts } from './conflicts-facade.js';
60
60
  import { upgradeReplaces, stopForUpgrade } from './version-guard.js';
@@ -364,6 +364,7 @@ class LiveSync {
364
364
  this._profilePromise = null;
365
365
  this._sharedData = false;
366
366
  this._sharedSync = false;
367
+ this._workerFeatures = ['live-data'];
367
368
  this._liveData = false;
368
369
  this._editing = null;
369
370
  this._presenceLease = null;
@@ -390,6 +391,7 @@ class LiveSync {
390
391
 
391
392
  // Store handler reference for cleanup
392
393
  this._snapshotHandler = null;
394
+ this._hiddenHandler = null;
393
395
 
394
396
  // High-water mark of server seqs we've seen on this channel (own echoes
395
397
  // count too). Server-broadcast payloads carry a monotonic seq
@@ -776,6 +778,12 @@ class LiveSync {
776
778
  this._settledHandler = null;
777
779
  }
778
780
 
781
+ if (this._hiddenHandler) {
782
+ document.removeEventListener('visibilitychange', this._hiddenHandler);
783
+ document.removeEventListener('clay:workspace-background', this._hiddenHandler);
784
+ this._hiddenHandler = null;
785
+ }
786
+
779
787
  this._queuedSend = null;
780
788
 
781
789
  // Cancel any pending frame and clear the queue. A morph already in
@@ -848,6 +856,7 @@ class LiveSync {
848
856
  this._sharedData = resolved.sharedData;
849
857
  this._sharedSync = resolved.sharedSync;
850
858
  this._liveData = resolved.liveData;
859
+ this._workerFeatures = resolved.workerFeatures;
851
860
  this._profile = resolved.profile;
852
861
  this._log(`Wire profile: ${resolved.profile.name}`);
853
862
  return resolved.profile;
@@ -925,15 +934,33 @@ class LiveSync {
925
934
  // profile is known, so this is never null on the normal path.
926
935
  const profile = this._profile || WIRE_PROFILES.legacy;
927
936
  const path = profile.streamPath(window.location.href, this.lane, this.resumeId, this.clientId);
937
+ // A frame scheduled while the page showed waits for an animation frame, which a
938
+ // page hidden since then never runs. Move it to the hidden schedule.
939
+ if (!this._hiddenHandler) {
940
+ this._hiddenHandler = () => {
941
+ if (this._rafHandle == null || !pageHidden()) return;
942
+ this._cancelFrame(this._rafHandle);
943
+ this._rafHandle = null;
944
+ this._scheduleNextFrame();
945
+ };
946
+ document.addEventListener('visibilitychange', this._hiddenHandler);
947
+ document.addEventListener('clay:workspace-background', this._hiddenHandler);
948
+ }
928
949
  // Resolved against the real origin: a <base href> in the authored document
929
950
  // would otherwise point the sync stream at an origin the document chose.
930
951
  this.sse = new SyncStream(new URL(path, window.location.origin).href, {
931
952
  shared: this._sharedSync,
932
953
  sharedData: this._sharedData,
954
+ workerFeatures: this._workerFeatures,
933
955
  documentURL: window.location.href,
934
956
  lane: this.lane,
935
957
  clientId: this.clientId,
936
- keepAliveWhenHidden: this._liveData && this.lane === 'live',
958
+ // A hidden edit tab stays subscribed wherever that costs no connection of its
959
+ // own: a live-data host keeps it for the room, and the shared worker carries it
960
+ // for free ('shared': the stream decides, from the connection it holds).
961
+ // Released, it would miss a peer's save, and its own next save would be
962
+ // refused as a conflict.
963
+ keepAliveWhenHidden: this.lane !== 'live' ? false : (this._liveData ? true : 'shared'),
937
964
  // Only a response that named its own version gives this tab anything to
938
965
  // compare against. On a host that served none there is no provenance to
939
966
  // check, and the page keeps its pre-1.9.0 startup behavior.
@@ -1305,7 +1332,7 @@ class LiveSync {
1305
1332
  // at: the save adds the format marker on its way out (core/save-core.js runSave),
1306
1333
  // after the capture this relay was built from. At floor 0 the html is unchanged.
1307
1334
  const sent = acceptedBytesFor(captureId) ?? acceptedHtml;
1308
- const floor = typeof sent === 'string' ? pageFormatFloor(sent) : 0;
1335
+ const floor = typeof sent === 'string' ? blockeditorFormatFloor(sent) : 0;
1309
1336
  payload = { ...payload, html: asSent(withPageFormatFloor(payload.html, Math.max(knownPageFormatFloor(), floor)), sent) };
1310
1337
 
1311
1338
  // Skip only an unstamped repeat, judged against the base as it stood. A
@@ -1919,19 +1946,35 @@ class LiveSync {
1919
1946
  return true;
1920
1947
  }
1921
1948
 
1949
+ // A hidden page runs no animation frames, and Chrome slows its timers to one a
1950
+ // second (one a minute after five minutes). A frame left waiting there kept the
1951
+ // stamp of the version before a peer's save, so the tab's next save was refused
1952
+ // as a conflict. A message the page posts to itself runs at once, hidden or not.
1922
1953
  _requestFrame(cb) {
1954
+ if (pageHidden()) {
1955
+ if (typeof MessageChannel === 'function') {
1956
+ const channel = new MessageChannel();
1957
+ let live = true;
1958
+ channel.port1.onmessage = () => {
1959
+ channel.port1.close();
1960
+ if (live) cb();
1961
+ };
1962
+ channel.port2.postMessage(null);
1963
+ return { cancel: () => { live = false; channel.port1.close(); } };
1964
+ }
1965
+ const timer = setTimeout(cb, 0);
1966
+ return { cancel: () => clearTimeout(timer) };
1967
+ }
1923
1968
  if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
1924
- return window.requestAnimationFrame(cb);
1969
+ const frame = window.requestAnimationFrame(cb);
1970
+ return { cancel: () => window.cancelAnimationFrame?.(frame) };
1925
1971
  }
1926
- return setTimeout(cb, 16);
1972
+ const timer = setTimeout(cb, 16);
1973
+ return { cancel: () => clearTimeout(timer) };
1927
1974
  }
1928
1975
 
1929
1976
  _cancelFrame(handle) {
1930
- if (typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function') {
1931
- window.cancelAnimationFrame(handle);
1932
- return;
1933
- }
1934
- clearTimeout(handle);
1977
+ handle?.cancel?.();
1935
1978
  }
1936
1979
 
1937
1980
  /**