@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
@@ -123,16 +123,16 @@ async function declaredRenames(htmlText) {
123
123
  const PAGE_STORE = 'script[clay~="live"][merge]';
124
124
 
125
125
  function carryPage(doc, afterDoc, article) {
126
- const target = afterDoc.querySelector("article[data-page]");
126
+ const target = afterDoc.querySelector("article[data-blockeditor]");
127
127
  if (!target) return null;
128
128
  target.replaceWith(afterDoc.importNode(article, true));
129
129
  for (const store of doc.querySelectorAll(PAGE_STORE)) {
130
130
  const name = store.getAttribute("merge");
131
- if (name !== "page" && !name.startsWith("page.")) continue;
131
+ if (name !== "blockeditor" && !name.startsWith("blockeditor.")) continue;
132
132
  const copy = afterDoc.importNode(store, true);
133
133
  const existing = [...afterDoc.querySelectorAll(PAGE_STORE)].find((el) => el.getAttribute("merge") === name);
134
134
  if (existing) existing.replaceWith(copy);
135
- else afterDoc.querySelector("article[data-page]").after(copy);
135
+ else afterDoc.querySelector("article[data-blockeditor]").after(copy);
136
136
  }
137
137
  const doctype = afterDoc.doctype ? `<!DOCTYPE ${afterDoc.doctype.name}>\n` : "";
138
138
  return doctype + afterDoc.documentElement.outerHTML;
@@ -209,7 +209,7 @@ export async function prepare(update, {
209
209
  }
210
210
  const afterDoc = new DOMParser().parseFromString(result.html, "text/html");
211
211
  const before = upgrade.extractAll(doc).dataByName;
212
- const article = doc.querySelector("article[data-page]");
212
+ const article = doc.querySelector("article[data-blockeditor]");
213
213
  if (!article && !Object.keys(before || {}).length) {
214
214
  return { ok: false, code: "undeclared", message: UNDECLARED };
215
215
  }
@@ -0,0 +1,23 @@
1
+ // The comment conventions an AI request shares: the payload ceiling, the @tokens that
2
+ // name a context file, and whether a comment asks for the saved page.
3
+
4
+ // The request ceiling, kept below the host's 1 MiB envelope so the refusal happens
5
+ // here, where the message can say what to do about it.
6
+ export const MAX_PAYLOAD_BYTES = 900 * 1024;
7
+
8
+ // Context refs are @tokens containing a dot or slash (@notes.md, @src/x.js). Bare
9
+ // @words are not refs: a leading one is an engine token (@fable, @codex — routed
10
+ // helper-side), and mid-text ones are prose.
11
+ // The @ has to start a word, so an email address or a URL is prose too.
12
+ export function contextRefsOf(comment) {
13
+ return [...String(comment ?? '').matchAll(/(?<![\w\u00C0-\u024F.@\/:-])@([\w.-]*[/.][\w./-]*)/g)]
14
+ .map(m => m[1].replace(/\.+$/, '')) // a sentence's full stop is not part of the name
15
+ .filter(ref => ref !== 'page')
16
+ .filter(ref => /[/.]/.test(ref));
17
+ }
18
+
19
+ // Every request that names @page reads the file from disk rather than a copy in the
20
+ // payload, so the caller has to save first and then ask for it by flag.
21
+ export function wantsPage(comment) {
22
+ return /(?<![\w\u00C0-\u024F.@\/:-])@page(?![\w\/-]|\.\w)/.test(String(comment ?? ''));
23
+ }
package/src/lib/effect.js CHANGED
@@ -115,8 +115,15 @@ export function effect(fn) {
115
115
  for (const type of undoEvents) clay.undo?.on?.(type, schedule);
116
116
  };
117
117
 
118
+ // Shared values the host sent back are applied with observers paused, to keep them out of
119
+ // undo, so this event is the only sign they changed the page.
120
+ const onSiteData = (event) => {
121
+ if (event.detail?.applied?.length) schedule();
122
+ };
123
+
118
124
  const offMutation = Mutation.onAnyChange({ debounce: 0, require: "observed" }, schedule);
119
125
  document.addEventListener("clay:sync-applied", schedule);
126
+ document.addEventListener("clay:site-data", onSiteData);
120
127
  document.addEventListener("clay:modechange", bindUndo);
121
128
  bindUndo();
122
129
 
@@ -124,6 +131,7 @@ export function effect(fn) {
124
131
  stopped = true;
125
132
  offMutation();
126
133
  document.removeEventListener("clay:sync-applied", schedule);
134
+ document.removeEventListener("clay:site-data", onSiteData);
127
135
  document.removeEventListener("clay:modechange", bindUndo);
128
136
  for (const type of undoEvents) clay.undo?.off?.(type, schedule);
129
137
  };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The failure code a thrown IndexedDB error means to a caller.
3
+ *
4
+ * Only one of these is about this device being full (`quota`); the rest are about
5
+ * the database being unreachable, refused, or mid-upgrade somewhere else. None of
6
+ * them is a save result, and none of them is durable.
7
+ *
8
+ * @param {*} error
9
+ * @returns {string}
10
+ */
11
+ export function codeFor(error) {
12
+ const name = error?.name || "";
13
+ if (name === "QuotaExceededError" || name === "NS_ERROR_DOM_QUOTA_REACHED") return "quota";
14
+ if (name === "VersionError") return "unknown-schema";
15
+ if (name === "DataCloneError") return "uncloneable";
16
+ if (name === "AbortError") return "aborted";
17
+ if (name === "TransactionInactiveError" || name === "InvalidStateError") return "aborted";
18
+ if (name === "SecurityError" || name === "UnknownError" || name === "NotSupportedError") return "unavailable";
19
+ return "error";
20
+ }
@@ -24,7 +24,7 @@ export const FRAME_BYTES = 63 * 1024;
24
24
  // What a host still on the old 16 KB body cap takes: a multi-op frame it
25
25
  // answers 413 is packed again at this size for the rest of the session.
26
26
  export const OLD_FRAME_BYTES = 15 * 1024;
27
- const PAGE_STORE = /^page(\.[0-9]+)?$/;
27
+ const PAGE_STORE = /^blockeditor(\.[0-9]+)?$/;
28
28
  const BUFFER_MAX = 512;
29
29
  const RETRY_MAX_MS = 8000;
30
30
  const REVOKED = new Set([401, 403]);
@@ -79,7 +79,7 @@ export function createFastLane({
79
79
  let synced = false;
80
80
  let epoch = null;
81
81
  let receiptGeneration = null;
82
- let pageGeneration = null;
82
+ let blockeditorGeneration = null;
83
83
  let provenanceVersion = 0;
84
84
  let seq = 0;
85
85
  let writer = null;
@@ -202,7 +202,7 @@ export function createFastLane({
202
202
  count++;
203
203
  }
204
204
  const take = lane.unsent.splice(0, count);
205
- const frame = { n: ++lane.n, ops: take.map((entry) => entry.op), entries: take, first: take[0].id, through: take[take.length - 1].id, seq: null, sent: false, receiptGeneration, pageGeneration };
205
+ const frame = { n: ++lane.n, ops: take.map((entry) => entry.op), entries: take, first: take[0].id, through: take[take.length - 1].id, seq: null, sent: false, receiptGeneration, blockeditorGeneration };
206
206
  markAdopted(lane, frame);
207
207
  lane.framed = Math.max(lane.framed, frame.through);
208
208
  lane.awaiting.push(frame);
@@ -231,7 +231,7 @@ export function createFastLane({
231
231
  lastSentAt = now();
232
232
  let result;
233
233
  try {
234
- result = await transport.send(clientId, { name, n: frame.n, ops: frame.ops, epoch: sentEpoch, ...(conn !== undefined && { conn }), ...(frame.receiptGeneration !== null && { receiptGeneration: frame.receiptGeneration }), ...(frame.pageGeneration !== null && PAGE_STORE.test(name) && { pageGeneration: frame.pageGeneration }), ...(frame.adopted && { adopted: frame.adopted }), ...(PAGE_STORE.test(name) && lane.compaction?.v === 1 && { compaction: 1 }) });
234
+ result = await transport.send(clientId, { name, n: frame.n, ops: frame.ops, epoch: sentEpoch, ...(conn !== undefined && { conn }), ...(frame.receiptGeneration !== null && { receiptGeneration: frame.receiptGeneration }), ...(frame.blockeditorGeneration !== null && PAGE_STORE.test(name) && { blockeditorGeneration: frame.blockeditorGeneration }), ...(frame.adopted && { adopted: frame.adopted }), ...(PAGE_STORE.test(name) && lane.compaction?.v === 1 && { compaction: 1 }) });
235
235
  } catch {
236
236
  result = null;
237
237
  }
@@ -255,7 +255,7 @@ export function createFastLane({
255
255
  frame.sent = false;
256
256
  frame.seq = null;
257
257
  if (frame.epoch !== epoch) frame.receiptGeneration = receiptGeneration;
258
- if (frame.epoch !== epoch) frame.pageGeneration = pageGeneration;
258
+ if (frame.epoch !== epoch) frame.blockeditorGeneration = blockeditorGeneration;
259
259
  return;
260
260
  }
261
261
  // Its store left the room meanwhile: its ops stay pending for the save,
@@ -494,9 +494,9 @@ export function createFastLane({
494
494
  const fresh = state.epoch !== epoch;
495
495
  provenanceVersion = Number.isInteger(state.provenanceVersion) ? state.provenanceVersion : 0;
496
496
  receiptGeneration = Number.isSafeInteger(state.receiptGeneration) ? state.receiptGeneration : null;
497
- const priorGeneration = pageGeneration;
498
- pageGeneration = typeof state.pageTransition?.id === "string" ? state.pageTransition.id : null;
499
- const replaced = pageGeneration !== priorGeneration;
497
+ const priorGeneration = blockeditorGeneration;
498
+ blockeditorGeneration = typeof state.blockeditorTransition?.id === "string" ? state.blockeditorTransition.id : null;
499
+ const replaced = blockeditorGeneration !== priorGeneration;
500
500
  let joined = false;
501
501
  // Every store is read whole here.
502
502
  missed = false;
@@ -573,7 +573,7 @@ export function createFastLane({
573
573
  frame.sent = false;
574
574
  frame.seq = null;
575
575
  frame.receiptGeneration = receiptGeneration;
576
- frame.pageGeneration = pageGeneration;
576
+ frame.blockeditorGeneration = blockeditorGeneration;
577
577
  }
578
578
  lane.awaiting = resend;
579
579
  entry.store.rebase(room.value, { source: "state", dropIds, room: { seq, fresh: true, moves: state.moves?.[name] ?? null, floor: Number.isInteger(room.floor) ? room.floor : null, compaction: lane.compaction } });
@@ -842,7 +842,7 @@ export function createFastLane({
842
842
  const take = pending.splice(0, count);
843
843
  const frame = { n: ++lane.n, ops: take.map((e) => e.op), entries: take,
844
844
  first: take[0].id, through: take[take.length - 1].id, seq: null,
845
- sent: false, refused: true, epoch, client: clientId, receiptGeneration, pageGeneration };
845
+ sent: false, refused: true, epoch, client: clientId, receiptGeneration, blockeditorGeneration };
846
846
  markAdopted(lane, frame);
847
847
  lane.awaiting.push(frame);
848
848
  lane.framed = frame.through;
@@ -68,11 +68,11 @@ const encoder = new TextEncoder();
68
68
  // Floor 1 moves the durable lane to its own channel scope: a tab that cannot read
69
69
  // floor 1 never receives future-format updates, and its writes are refused by the
70
70
  // host floor. The message protocol inside the scope is unchanged.
71
- export const FORMAT_SCOPE = "page-format:1";
71
+ export const FORMAT_SCOPE = "blockeditor-format:1";
72
72
 
73
73
  // The room scope: this lane's sequencer is a host-mode room, so a tab of the lane
74
74
  // before it, which cannot read that room, is never on the same channel or lock.
75
- export const ROOM_SCOPE = "page-rooms:2";
75
+ export const ROOM_SCOPE = "blockeditor-rooms:2";
76
76
 
77
77
  export function localLaneKey(floor = 0, path = window.location.pathname) {
78
78
  const base = "clay-live:" + path;
@@ -193,7 +193,7 @@ export function localTransport({ seed, key = "clay-live:" + window.location.path
193
193
  const meta = {};
194
194
  for (const [name, m] of mirror.stores) {
195
195
  stores[name] = { value: cloneJson(m.value), ephemeral: m.ephemeral, seen: receiptsFor(name), last: m.last, landed: landedIn(m) };
196
- if (name === "page" && isYValue(m.value)) stores[name].compaction = { v: COMPACTION.version, loose: COMPACTION.loose, activeBytes: COMPACTION.activeBytes };
196
+ if (name === "blockeditor" && isYValue(m.value)) stores[name].compaction = { v: COMPACTION.version, loose: COMPACTION.loose, activeBytes: COMPACTION.activeBytes };
197
197
  meta[name] = { log: m.log, last: m.last, landed: m.landed, floor: m.floor, behind: m.behind, file: m.file, inFile: m.inFile };
198
198
  }
199
199
  const state = { epoch: room.epoch, seq: room.seq, writer: clientId, stores, mirror: { seen: [...mirror.seen], stores: meta } };
@@ -386,7 +386,7 @@ export function localTransport({ seed, key = "clay-live:" + window.location.path
386
386
  const pageHost = () => ({ retired: new Set(), lastFull: null, deletesSince: 0, failed: null, ranges: createRangeCache(), task: null, overflow: false });
387
387
 
388
388
  const pageStore = () => {
389
- const m = mirror.stores.get("page");
389
+ const m = mirror.stores.get("blockeditor");
390
390
  return m && !m.ephemeral && isYValue(m.value) ? m : null;
391
391
  };
392
392
 
@@ -471,14 +471,14 @@ export function localTransport({ seed, key = "clay-live:" + window.location.path
471
471
  host.lastFull = { bytes, seq: room.seq };
472
472
  host.deletesSince = 0;
473
473
  }
474
- const data = { epoch: room.epoch, seq: room.seq, name: "page", ops, from: "compactor", n: 0, kind: plan.kind };
474
+ const data = { epoch: room.epoch, seq: room.seq, name: "blockeditor", ops, from: "compactor", n: 0, kind: plan.kind };
475
475
  advance(data);
476
476
  if (clean) m.landed = room.seq;
477
477
  post({ type: "data", data });
478
478
  handlers.frame(cloneJson(data));
479
479
  if (clean) {
480
- tellLanded(["page"]);
481
- post({ type: "landed", name: "page", at: { epoch: room.epoch, seq: room.seq } });
480
+ tellLanded(["blockeditor"]);
481
+ post({ type: "landed", name: "blockeditor", at: { epoch: room.epoch, seq: room.seq } });
482
482
  }
483
483
  const shape = pageShape(m.value.y, COMPACTION);
484
484
  if (shape.loose >= COMPACTION.soft || !shape.normal) due();
@@ -502,7 +502,7 @@ export function localTransport({ seed, key = "clay-live:" + window.location.path
502
502
  return { status: 422, body: { error: err.message }, retryAfter: 0 };
503
503
  }
504
504
  const host = room.host;
505
- const admission = frame.name === "page" && isYValue(m.value) ? admitPageOps({ y: m.value.y, ops, state: { ...host, compaction: frame.compaction }, limits: COMPACTION }) : null;
505
+ const admission = frame.name === "blockeditor" && isYValue(m.value) ? admitPageOps({ y: m.value.y, ops, state: { ...host, compaction: frame.compaction }, limits: COMPACTION }) : null;
506
506
  if (admission?.error) {
507
507
  if (admission.error === "checkpoint-required") due();
508
508
  return refused(admission.error, admission.retryAfter);
@@ -516,7 +516,7 @@ export function localTransport({ seed, key = "clay-live:" + window.location.path
516
516
  const bytes = admission ? pageBytes(trial.value) : 0;
517
517
  trial.undo();
518
518
  if (admission && bytes > admission.limit) {
519
- if (admission.failed || (host.lastFull && host.deletesSince === 0)) return refused("page-full");
519
+ if (admission.failed || (host.lastFull && host.deletesSince === 0)) return refused("blockeditor-full");
520
520
  due(true);
521
521
  return refused("checkpoint-required");
522
522
  }
@@ -572,7 +572,7 @@ export function localTransport({ seed, key = "clay-live:" + window.location.path
572
572
  }
573
573
  if (value === null || typeof value !== "object" || Array.isArray(value) !== Array.isArray(m.value)) return null;
574
574
  const file = { seq: seq ?? null, hash: textHash(serializeLive(value)) };
575
- if (name === "page" && isYValue(m.value) && isYValue(value)) return mergePageFile(m, value, file);
575
+ if (name === "blockeditor" && isYValue(m.value) && isYValue(value)) return mergePageFile(m, value, file);
576
576
  // A stamp of this room places the file. Still the value its stamp names, it
577
577
  // is a capture and holds no merge past the stamp; edited, or unstamped, it
578
578
  // descends from the file before it, which holds the merges in inFile.
@@ -16,8 +16,8 @@ export const COMPACTION = Object.freeze({
16
16
  });
17
17
 
18
18
  export const ROOM_ERROR_STATUS = Object.freeze({
19
- 'checkpoint-required': 503, 'page-full': 409, 'compaction-failed': 409,
20
- 'page-immutable': 409, 'page-batch': 409, 'page-update-invalid': 422, 'page-upgrade': 404,
19
+ 'checkpoint-required': 503, 'blockeditor-full': 409, 'compaction-failed': 409,
20
+ 'blockeditor-immutable': 409, 'blockeditor-batch': 409, 'blockeditor-update-invalid': 422, 'blockeditor-upgrade': 404,
21
21
  });
22
22
 
23
23
  export const CLIENT_KEY = /^[ur][0-9a-z]+(-[0-9a-z]+){2}$/;
@@ -98,25 +98,25 @@ export function checkUpdate(text) {
98
98
  }
99
99
 
100
100
  export function admitPageOps({ y, ops, state, limits }) {
101
- if (state.compaction !== limits.version) return { error: 'page-upgrade' };
101
+ if (state.compaction !== limits.version) return { error: 'blockeditor-upgrade' };
102
102
  let added = 0;
103
103
  let deleted = 0;
104
104
  let inserts = false;
105
105
  for (const op of ops) {
106
106
  const key = op.path?.[1];
107
- if (op.op !== 'set' || typeof key !== 'string' || !CLIENT_KEY.test(key)) return { error: 'page-immutable' };
107
+ if (op.op !== 'set' || typeof key !== 'string' || !CLIENT_KEY.test(key)) return { error: 'blockeditor-immutable' };
108
108
  if (Object.hasOwn(y, key)) {
109
- if (y[key] !== op.value) return { error: 'page-immutable' };
109
+ if (y[key] !== op.value) return { error: 'blockeditor-immutable' };
110
110
  continue;
111
111
  }
112
- if (state.retired.has(key)) return { error: 'page-immutable' };
112
+ if (state.retired.has(key)) return { error: 'blockeditor-immutable' };
113
113
  const checked = checkUpdate(op.value);
114
- if (!checked) return { error: 'page-update-invalid' };
114
+ if (!checked) return { error: 'blockeditor-update-invalid' };
115
115
  added += 1;
116
116
  deleted += checked.deleted;
117
117
  inserts ||= checked.inserts;
118
118
  }
119
- if (added > limits.newPerFrame) return { error: 'page-batch' };
119
+ if (added > limits.newPerFrame) return { error: 'blockeditor-batch' };
120
120
  // A page whose compaction failed keeps taking edits up to its byte limit, as a room
121
121
  // without compaction does, and refuses past it as full instead of waiting for a fold.
122
122
  if (state.failed) return { added, deleted, inserts, limit: limits.activeBytes + (inserts ? 0 : limits.deleteReserve), shape: null, failed: true };
@@ -5,7 +5,7 @@ import { PAGE_FORMAT, PAGE_FORMAT_HEADER } from "../core/page-format.js";
5
5
  // one POST per frame of ops, one GET for the room's whole state. Both name the
6
6
  // document with Document-URL, as the sync relay and presence do, and both declare
7
7
  // the page format this client writes: registration, operation and checkpoint are
8
- // page-writing paths, so each one carries the capability the floor decides on.
8
+ // blockeditor-writing paths, so each one carries the capability the floor decides on.
9
9
 
10
10
  const DATA_PATH = "/_/sync/data";
11
11
 
@@ -39,10 +39,10 @@ export const PLUGIN_PATHS = {
39
39
  // A live JSON tag as an in-memory object (clay.live). Not editOnly: a reader's
40
40
  // tab still shows the game, and its writes stay in that tab.
41
41
  live: { path: "plugins/live.js", editOnly: false, default: false },
42
- page: { path: "plugins/page.js", editOnly: true, default: false },
43
- // The reader half of `page`, added by resolveModules for a Document in view mode
42
+ blockeditor: { path: "plugins/page.js", editOnly: true, default: false },
43
+ // The reader half of `blockeditor`, added by resolveModules for a Document in view mode
44
44
  // and never named in a URL: the reader's article follows each saved file.
45
- "page-view": { path: "plugins/page-view.js", editOnly: false, default: false, internal: true },
45
+ "blockeditor-view": { path: "plugins/page-view.js", editOnly: false, default: false, internal: true },
46
46
  sortable: { path: "plugins/sortable.js", editOnly: true, default: false },
47
47
  undo: { path: "plugins/undo.js", editOnly: true, default: false },
48
48
  unsent: { path: "plugins/unsent.js", editOnly: true, default: false },
@@ -118,6 +118,7 @@ export const MODULES = {
118
118
  "core/snapshot.js": () => import("./core/snapshot.js"),
119
119
  "core/save-core.js": () => import("./core/save-core.js"),
120
120
  "core/save.js": () => import("./core/save.js"),
121
+ "core/site-edits.js": () => import("./core/site-edits.js"),
121
122
  "core/conflict-notice.js": () => import("./core/conflict-notice.js"),
122
123
  "core/recovery.js": () => import("./core/recovery.js"),
123
124
  "core/unsaved-warning.js": () => import("./core/unsaved-warning.js"),
@@ -159,7 +160,7 @@ export const MODULES = {
159
160
 
160
161
  // `source` is last on purpose: its install captures a save clone, so it wants every
161
162
  // plugin that registers a document transform to have registered it first.
162
- const PLUGIN_ORDER = ["visitor-drafts", "visitor-menu", "richclay", "indicator", "sortable", "undo", "unsent", "quickcrop", "upload", "people", "menu", "upgrade", "cms", "page-view", "sync", "live", "page", "message", "like", "wire", "ai-edit", "demo", "derive", "bind-text", "source"];
163
+ const PLUGIN_ORDER = ["visitor-drafts", "visitor-menu", "richclay", "indicator", "sortable", "undo", "unsent", "quickcrop", "upload", "people", "menu", "upgrade", "cms", "blockeditor-view", "sync", "live", "blockeditor", "message", "like", "wire", "ai-edit", "demo", "derive", "bind-text", "source"];
163
164
 
164
165
  // A plugin that cannot do its whole job alone. hypercms reads the cropper through
165
166
  // a capability lookup (`clay.quickcrop`) and silently uploads the raw file when it
@@ -189,15 +190,22 @@ const PLUGIN_ORDER = ["visitor-drafts", "visitor-menu", "richclay", "indicator",
189
190
  // The menu is how a person opens the CMS: hypercms adds no button of its own. It loads
190
191
  // in view too, because that is where a person starts, and it shows itself only to
191
192
  // someone who can edit. A page with its own control uses exclude=menu.
192
- const IMPLIES = { cms: ["quickcrop", "upload", "menu"], "ai-edit": ["wire"], page: ["live", "sync", "upload"] };
193
+ const IMPLIES = { cms: ["quickcrop", "upload", "menu"], "ai-edit": ["wire"], blockeditor: ["live", "sync", "upload", "wire", "ai-edit"] };
193
194
 
194
195
  // A Document's editor owns its article's rich text, so richclay (for [editable]) has
195
- // nothing to do there; plugins=page,richclay still loads it.
196
- const DISPLACES = { page: ["richclay"] };
196
+ // nothing to do there; plugins=blockeditor,richclay still loads it.
197
+ const DISPLACES = { blockeditor: ["richclay"] };
198
+ // A Document's editor keeps its own history, the same one Cmd+Z in the editor uses, and
199
+ // hyper-undo takes Cmd+Z for the whole window: loaded together the two would fight over
200
+ // every press. This is the one pair where a name the page asked for by hand still loses.
201
+ const CANNOT_RUN_WITH = { blockeditor: ["undo"] };
197
202
  // No first paint or keystroke waits for these. source keeps the file's bytes outside
198
203
  // the edited parts stable; a save before it installs is the ordinary full serialization.
199
204
  // upgrade only asks the host for a newer version once `clay.ready` resolves.
205
+ // The editor half of `blockeditor` owns AI editing, so only a page that asked for it can wait
206
+ // for the wire and the composer; everywhere else they are ordinary plugins.
200
207
  const LATE = new Set(["source", "sortable", "menu", "upgrade"]);
208
+ const LATE_WITH_PAGE = new Set(["wire", "ai-edit"]);
201
209
 
202
210
  function parseCsv(params, key, enabled, apply) {
203
211
  const raw = params.get(key);
@@ -214,8 +222,15 @@ function parseCsv(params, key, enabled, apply) {
214
222
  }
215
223
 
216
224
  // Each of these talks to the host or writes outside the visitor's own record, so a
217
- // page that keeps its edits in this browser cannot carry them.
218
- const DRAFT_DROPS = ["sync", "live", "page", "upload", "quickcrop", "cms", "people", "menu", "upgrade", "wire", "ai-edit", "demo", "source"];
225
+ // page that keeps its edits in this browser cannot carry them. A page document keeps
226
+ // page and live: the editor runs on a store this browser keeps.
227
+ const DRAFT_DROPS = ["sync", "live", "blockeditor", "upload", "quickcrop", "cms", "people", "menu", "upgrade", "wire", "ai-edit", "demo", "source"];
228
+
229
+ // The one core module a document asks for by what it declares: shared site values are
230
+ // tracked only on a page whose rules tag names them, and only where a save reaches the
231
+ // host (a visitor's draft is saved into this browser). It goes after the save modules,
232
+ // which it registers with. No other document ever fetches it.
233
+ export const SITE_EDITS = "core/site-edits.js";
219
234
 
220
235
  // [refetch-on-save] swaps its element for a fresh GET of the same URL after every
221
236
  // save, which on a visitor's own draft is a request to the host. The conflict notice
@@ -225,9 +240,10 @@ const DRAFT_DROPS_CORE = ["attrs/refetch-on-save.js", "core/conflict-notice.js"]
225
240
  // Only that host's copies auto-add the update check; another source names it.
226
241
  const TEMPLATE_SOURCE_PREFIX = "https://hyperclay.com/templates/";
227
242
 
228
- export function resolveModules(params, isEditMode, { isDraftMode = false, hasSap = false, hasVariants = false, canEdit = false, templateSource = null } = {}) {
243
+ export function resolveModules(params, isEditMode, { isDraftMode = false, hasSap = false, hasVariants = false, hasSite = false, canEdit = false, templateSource = null } = {}) {
229
244
  const core = [...CORE_WAVES.always];
230
245
  if (isEditMode) core.push(...CORE_WAVES.editOnly);
246
+ if (isEditMode && !isDraftMode && hasSite) core.splice(core.indexOf("core/save.js") + 1, 0, SITE_EDITS);
231
247
 
232
248
  const explicit = new Set();
233
249
  parseCsv(params, "plugins", explicit, (set, name) => set.add(name));
@@ -241,23 +257,27 @@ export function resolveModules(params, isEditMode, { isDraftMode = false, hasSap
241
257
  if (hasSap) enabled.add("derive");
242
258
  // Two or more looks need the dock's variant pill, and only someone who can edit sees it.
243
259
  if (hasVariants && (isEditMode || canEdit)) enabled.add("menu");
244
- // ai-edit cannot run without the wire, so excluding the wire excludes it too.
245
- if (excluded.has("wire")) enabled.delete("ai-edit");
246
- if (!isEditMode && !isDraftMode && enabled.has("page")) enabled.add("page-view");
260
+ if (!isEditMode && !isDraftMode && enabled.has("blockeditor")) enabled.add("blockeditor-view");
247
261
  // A plugin this mode does not load brings nothing with it, whether a default put
248
262
  // it in the set or the tag named it. A page that wants what an edit-only plugin
249
- // implies in view mode names that plugin too. `page` is the case that matters
263
+ // implies in view mode names that plugin too. `blockeditor` is the case that matters
250
264
  // most: a reader loads its reader half and no generic sync, live data service or
251
- // upload, so `plugins=page` in view mode resolves to page-view alone and the
252
- // saved transport page-view owns is the only stream.
265
+ // upload, so `plugins=blockeditor` in view mode resolves to blockeditor-view alone and the
266
+ // saved transport blockeditor-view owns is the only stream.
253
267
  for (const name of [...enabled]) {
254
268
  if (excluded.has(name)) continue;
255
269
  if (PLUGIN_PATHS[name].editOnly && !isEditMode) continue;
256
270
  for (const implied of IMPLIES[name] || []) enabled.add(implied);
257
271
  }
272
+ // ai-edit cannot run without the wire, so excluding the wire excludes it too. After
273
+ // the implications, or a page that implies ai-edit would re-add it without a wire.
274
+ if (excluded.has("wire")) enabled.delete("ai-edit");
258
275
  for (const name of excluded) enabled.delete(name);
259
276
  if (isDraftMode) {
260
- for (const name of DRAFT_DROPS) enabled.delete(name);
277
+ // A page document keeps its editor and the store under it: a visitor edits the real
278
+ // page, and the save pipeline's draft transport is its only host.
279
+ const keepsPage = enabled.has("blockeditor");
280
+ for (const name of DRAFT_DROPS) if (!(keepsPage && (name === "blockeditor" || name === "live"))) enabled.delete(name);
261
281
  for (const path of DRAFT_DROPS_CORE) {
262
282
  const at = core.indexOf(path);
263
283
  if (at > -1) core.splice(at, 1);
@@ -276,13 +296,23 @@ export function resolveModules(params, isEditMode, { isDraftMode = false, hasSap
276
296
  if (enabled.has(owner)) for (const name of names) if (!explicit.has(name)) enabled.delete(name);
277
297
  }
278
298
 
299
+ for (const [owner, names] of Object.entries(CANNOT_RUN_WITH)) {
300
+ if (!enabled.has(owner)) continue;
301
+ for (const name of names) {
302
+ if (!enabled.has(name)) continue;
303
+ enabled.delete(name);
304
+ console.warn(`clayjs: plugin "${name}" cannot run with "${owner}" and was left out`);
305
+ }
306
+ }
307
+
279
308
  const plugins = [];
280
309
  const late = [];
281
310
  for (const name of PLUGIN_ORDER) {
282
311
  if (!enabled.has(name)) continue;
283
312
  const spec = PLUGIN_PATHS[name];
284
313
  if ((spec.editOnly && !isEditMode) || (spec.draft && !isDraftMode)) continue;
285
- (LATE.has(name) ? late : plugins).push(spec.ownerPath && !isDraftMode ? spec.ownerPath : spec.path);
314
+ const lateHere = LATE.has(name) || (LATE_WITH_PAGE.has(name) && enabled.has("blockeditor"));
315
+ (lateHere ? late : plugins).push(spec.ownerPath && !isDraftMode ? spec.ownerPath : spec.path);
286
316
  }
287
317
  return { core, plugins, late };
288
318
  }
package/src/loader.js CHANGED
@@ -87,10 +87,13 @@ export async function boot(base, params, readyResolve, manifest = null, suppleme
87
87
 
88
88
  const hasSap = Boolean(document.querySelector("[sap]"));
89
89
  const hasVariants = document.querySelectorAll("head style[clay-variant]").length >= 2;
90
+ // A page of a multi-page site names the values every page shares under `site` in its
91
+ // rules tag. core/site-edits.js makes the same test before it does anything.
92
+ const hasSite = /(?:^|[{,])\s*["']?site["']?\s*:/.test(document.querySelector('script[data-rules-name~="api"]')?.textContent ?? "");
90
93
  // derive.js imports the sapjs core only when no sap.js script took the page over.
91
94
  const hasSapJs = Boolean(document.querySelector('script[src*="sap.js"]'));
92
95
  bootPhase.holdSettle = hasSap && isEditMode;
93
- const plan = resolveModules(params, isEditMode, { isDraftMode, hasSap, hasVariants, canEdit, templateSource: templateSource() });
96
+ const plan = resolveModules(params, isEditMode, { isDraftMode, hasSap, hasVariants, hasSite, canEdit, templateSource: templateSource() });
94
97
  // Where edits land, resolved once: a visitor draft saves into its draft slot, the
95
98
  // demo plugin into localStorage, and everything else to the host. setMode takes no
96
99
  // destination, and V4 has no draft-to-host transition.
@@ -127,13 +130,13 @@ export async function boot(base, params, readyResolve, manifest = null, suppleme
127
130
  // Nothing is fetched for it here: the decision and the controller behind it wait for the
128
131
  // first switch, so this costs a reader with a declaration no more than one attribute.
129
132
  if (document.documentElement.getAttribute("clay-mode-switch") === "live") {
130
- const editing = resolveModules(params, true, { isDraftMode, hasSap, hasVariants, canEdit, templateSource: templateSource() });
133
+ const editing = resolveModules(params, true, { isDraftMode, hasSap, hasVariants, hasSite, canEdit, templateSource: templateSource() });
131
134
  loaded["core/edit-mode.js"].noteLiveSwitch({
132
135
  edit: editing,
133
136
  // The plan a view boot would have resolved, not the edit plan this boot resolved: an
134
137
  // exit imports it and starts its reader, and an edit-only module belongs to the
135
138
  // session being left.
136
- view: resolveModules(params, false, { isDraftMode, hasSap, hasVariants, canEdit, templateSource: templateSource() }),
139
+ view: resolveModules(params, false, { isDraftMode, hasSap, hasVariants, hasSite, canEdit, templateSource: templateSource() }),
137
140
  loaded,
138
141
  modules: MODULES,
139
142
  // A live entry attaches the edit plugins after boot: clay.plugins names them from then on.
@@ -154,7 +157,7 @@ export async function boot(base, params, readyResolve, manifest = null, suppleme
154
157
  await loaded["core/edit-mode.js"].activateInitialMode();
155
158
  window.clay.plugins = Object.freeze([...plan.plugins, ...plan.late].map((path) => PLUGIN_NAMES[path]));
156
159
  // What an edit session here would load, so view-mode chrome can offer an edit-only feature.
157
- const editPlan = isEditMode ? plan : resolveModules(params, true, { isDraftMode, hasSap, hasVariants, canEdit, templateSource: templateSource() });
160
+ const editPlan = isEditMode ? plan : resolveModules(params, true, { isDraftMode, hasSap, hasVariants, hasSite, canEdit, templateSource: templateSource() });
158
161
  window.clay.editPlugins = Object.freeze([...editPlan.plugins, ...editPlan.late].map((path) => PLUGIN_NAMES[path]));
159
162
 
160
163
  for (const path of plan.plugins) {
@@ -171,6 +174,8 @@ export async function boot(base, params, readyResolve, manifest = null, suppleme
171
174
  continue;
172
175
  }
173
176
  loaded[path] = mod;
177
+ // A draft held only in the database goes onto the page before a plugin that reads the page loads.
178
+ if (path === "plugins/visitor-drafts.js") await mod.restoring;
174
179
  if (path === "plugins/page-view.js") mod.start(readerOptions);
175
180
  attachPluginMember(path, mod, loaded); // immediately, not after the loop: hypercms's ?cms=true
176
181
  // auto-open runs as a microtask queued during ITS evaluation
@@ -209,7 +214,7 @@ const stale = (path, editMode) =>
209
214
  // degrades to absent as at boot.
210
215
  async function loadLate(paths, editMode) {
211
216
  try {
212
- await window.clay.page?.ready;
217
+ await window.clay.blockeditor?.ready;
213
218
  } catch {}
214
219
  for (const path of paths) {
215
220
  if (stale(path, editMode)) continue;
@@ -357,7 +362,7 @@ function attachPluginMember(path, mod, namespaces = loaded) {
357
362
  } else if (path === "plugins/live.js") {
358
363
  clay.live = mod.live;
359
364
  } else if (path === "plugins/page.js") {
360
- clay.page = mod.page;
365
+ clay.blockeditor = mod.page;
361
366
  } else if (path === "plugins/upgrade.js") {
362
367
  clay.save = clay.save || {};
363
368
  clay.save.replace = mod.replace;
@@ -201,7 +201,7 @@ export function installAdoption({
201
201
  if (!outcome.ok || outcome.outcome !== "adopted") return outcome;
202
202
  requestCheckpoint();
203
203
  notice(outcome.counts);
204
- doc.dispatchEvent(new CustomEvent("clay:page-adopted", { detail: outcome.counts }));
204
+ doc.dispatchEvent(new CustomEvent("clay:blockeditor-adopted", { detail: outcome.counts }));
205
205
  return outcome;
206
206
  });
207
207
  return queue;
package/src/page/adopt.js CHANGED
@@ -13,7 +13,7 @@ import { DEFAULTS, project, canon, revOf, parseModel, renderModel, fragmentClean
13
13
  import { unflatten } from "./inline.js";
14
14
  import { blocksFromPageValues } from "./ybind/decode.js";
15
15
 
16
- export const ADOPT = "page-adopt";
16
+ export const ADOPT = "blockeditor-adopt";
17
17
  const same = (a, b) => canon(a) === canon(b);
18
18
  const body = ({ id, parent, ...rest }) => ({ ...rest, parent: parent ?? null });
19
19
 
@@ -85,7 +85,7 @@ const opaqueNode = (schema, id, block) => {
85
85
 
86
86
  // The editor’s blocks as the article and the record state them: an opaque node
87
87
  // stands for the record it carries, never for a block type of its own.
88
- const recordsOf = (blocks) => Object.fromEntries(Object.entries(blocks).map(([id, b]) => {
88
+ export const recordsOf = (blocks) => Object.fromEntries(Object.entries(blocks).map(([id, b]) => {
89
89
  if (b.t !== "opaque_block") return [id, b];
90
90
  const { t: _t, raw, ...rest } = b;
91
91
  const { t, ...fields } = raw ?? {};