@panphora/clayjs 1.23.0 → 1.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (471) hide show
  1. package/dist/all.js +1 -1
  2. package/dist/blockeditor/themes/light-1.css +431 -0
  3. package/dist/clay-dom.js +1 -1
  4. package/dist/clay-events.js +1 -1
  5. package/dist/clay-internals.js +1 -1
  6. package/dist/clay-options.js +1 -1
  7. package/dist/clay-router.js +1 -1
  8. package/dist/clay-ui.js +1 -1
  9. package/dist/clay-utils.js +1 -1
  10. package/dist/clay.js +1 -1
  11. package/dist/clay.standalone.js +30285 -26613
  12. package/dist/m/c-277QP5ET.js +1 -0
  13. package/dist/m/c-2BLVA6IN.js +5 -0
  14. package/dist/m/c-2OJMOUPC.js +1 -0
  15. package/dist/m/c-2PVGJFI5.js +1 -0
  16. package/dist/m/{c-AYVGD6A6.js → c-3UW5EJ77.js} +1 -1
  17. package/dist/m/{c-T5NK2TVY.js → c-42ZNV7LI.js} +1 -1
  18. package/dist/m/{c-3TLG3OLG.js → c-46LJAZZJ.js} +1 -1
  19. package/dist/m/c-47IBJGPQ.js +1 -0
  20. package/dist/m/c-5FXEKU4Y.js +1 -0
  21. package/dist/m/c-5KXDKC5G.js +1 -0
  22. package/dist/m/{c-EUGJVHDD.js → c-5RZNFTWD.js} +1 -1
  23. package/dist/m/c-65LJU2H2.js +72 -0
  24. package/dist/m/{c-NQJRVJ56.js → c-6IJEVE6B.js} +1 -1
  25. package/dist/m/{c-NW54NHKE.js → c-6LEYVQJK.js} +1 -1
  26. package/dist/m/{c-GSRLXYWL.js → c-6Q6RGFBF.js} +1 -1
  27. package/dist/m/c-6RGEQNVV.js +1 -0
  28. package/dist/m/c-7CM4GMTS.js +6 -0
  29. package/dist/m/c-7NCWENHV.js +1 -0
  30. package/dist/m/c-7V6MGMX2.js +1 -0
  31. package/dist/m/{c-LKSL4AUJ.js → c-7WZSUTYP.js} +1 -1
  32. package/dist/m/c-A4L7APIU.js +1 -0
  33. package/dist/m/c-APGHGDZM.js +1 -0
  34. package/dist/m/c-AT7GLQGV.js +34 -0
  35. package/dist/m/{c-ZRXJD4G6.js → c-B7GOFGQR.js} +1 -1
  36. package/dist/m/{c-KDXIJEEF.js → c-BGN7RCTJ.js} +1 -1
  37. package/dist/m/c-BKM46LUJ.js +1 -0
  38. package/dist/m/c-BOMQIQ3V.js +1 -0
  39. package/dist/m/{c-62HZW5DH.js → c-C5AKJVRH.js} +1 -1
  40. package/dist/m/{c-BXYSDKAN.js → c-CJ7QYEWI.js} +1 -1
  41. package/dist/m/c-CTGRDT6E.js +1 -0
  42. package/dist/m/c-CV4T65JE.js +6 -0
  43. package/dist/m/c-CVSWLX3X.js +1 -0
  44. package/dist/m/c-CZHTXGJS.js +1 -0
  45. package/dist/m/c-D7NT5S4F.js +1 -0
  46. package/dist/m/c-DD26VXAP.js +1 -0
  47. package/dist/m/{c-ANNFZFLN.js → c-DDS23EY6.js} +1 -1
  48. package/dist/m/{c-ALDVFPWL.js → c-DFKBGSN2.js} +1 -1
  49. package/dist/m/c-DIKR7H57.js +147 -0
  50. package/dist/m/c-DXPOFLEJ.js +1 -0
  51. package/dist/m/c-EDE45NM7.js +1 -0
  52. package/dist/m/c-EILLN2H2.js +1 -0
  53. package/dist/m/c-EODTTNVA.js +1 -0
  54. package/dist/m/{c-G6COVIZB.js → c-ESK4S67I.js} +1 -1
  55. package/dist/m/c-FDWR3FL7.js +1 -0
  56. package/dist/m/{c-WQZTI5P2.js → c-FEC6LGFF.js} +1 -1
  57. package/dist/m/c-FLEIU6CG.js +1 -0
  58. package/dist/m/c-FQAQRXGK.js +1 -0
  59. package/dist/m/{c-W4JCVLAB.js → c-FSQXFG5T.js} +1 -1
  60. package/dist/m/c-FVPNX2GO.js +135 -0
  61. package/dist/m/c-FYDKYW6E.js +1 -0
  62. package/dist/m/c-GNI7WHKF.js +32 -0
  63. package/dist/m/c-GO635CRU.js +3 -0
  64. package/dist/m/c-H3FNLBHN.js +2 -0
  65. package/dist/m/c-HDVH3MVC.js +72 -0
  66. package/dist/m/c-HIX5H5L7.js +1 -0
  67. package/dist/m/c-HKSLOVU4.js +1 -0
  68. package/dist/m/{c-SGRIYOAJ.js → c-HL6BLJNA.js} +1 -1
  69. package/dist/m/c-HTLYXCUH.js +1 -0
  70. package/dist/m/{c-QIR5CAKM.js → c-IUL7PU4P.js} +1 -1
  71. package/dist/m/c-IXO7EI2Q.js +1 -0
  72. package/dist/m/{c-W5E5P2TZ.js → c-JQF2EUCG.js} +1 -1
  73. package/dist/m/{c-NEL7HQW3.js → c-JWDCQL6J.js} +1 -1
  74. package/dist/m/c-KQKKIVLW.js +20 -0
  75. package/dist/m/c-LATNQ6Q5.js +1 -0
  76. package/dist/m/c-LD6UVV5R.js +1 -0
  77. package/dist/m/c-LS7IVQVW.js +1 -0
  78. package/dist/m/{c-JCOQVZYS.js → c-LSVN3OLG.js} +1 -1
  79. package/dist/m/{c-JDZDF2QM.js → c-LTSPHVAK.js} +1 -1
  80. package/dist/m/c-MFOSMAVM.js +3 -0
  81. package/dist/m/c-MJ7GVE46.js +1 -0
  82. package/dist/m/c-MP6S4AZU.js +6 -0
  83. package/dist/m/c-MQKSKQHG.js +2 -0
  84. package/dist/m/{c-UHUNXWKN.js → c-MQYEDF3Q.js} +1 -1
  85. package/dist/m/{c-IHUWUKIU.js → c-MR75YPRT.js} +1 -1
  86. package/dist/m/c-ND3Q2G2I.js +1 -0
  87. package/dist/m/c-NEQRFC4O.js +1 -0
  88. package/dist/m/{c-5FKRZNJV.js → c-NFQFV7OP.js} +1 -1
  89. package/dist/m/c-NHXOBD6A.js +1 -0
  90. package/dist/m/{c-QIKT6SKM.js → c-NICICT4P.js} +1 -1
  91. package/dist/m/c-NQAPWM7O.js +1 -0
  92. package/dist/m/{c-QUGCKMWF.js → c-NVU3DORS.js} +1 -1
  93. package/dist/m/{c-LWIDDWAR.js → c-OCYG4NBQ.js} +22 -22
  94. package/dist/m/c-OH2FPYSY.js +19 -0
  95. package/dist/m/c-OYHI67B6.js +1 -0
  96. package/dist/m/{c-R2XUODVY.js → c-OYW5MFPZ.js} +1 -1
  97. package/dist/m/{c-5B4YBZXC.js → c-P2HKMJAW.js} +1 -1
  98. package/dist/m/{c-TSBVMWAH.js → c-PCNJETUA.js} +1 -1
  99. package/dist/m/c-PDBNCJCT.js +1 -0
  100. package/dist/m/c-PGSBXHUE.js +1 -0
  101. package/dist/m/{c-5TIZ2I64.js → c-PYG2NGWU.js} +1 -1
  102. package/dist/m/c-Q26XHBLQ.js +1 -0
  103. package/dist/m/c-Q6BG3ZLO.js +1 -0
  104. package/dist/m/c-QENKN7QI.js +1 -0
  105. package/dist/m/c-QQFHEODI.js +1 -0
  106. package/dist/m/c-QTEUO2K6.js +1 -0
  107. package/dist/m/c-R23UHMXG.js +1 -0
  108. package/dist/m/c-RETTCOWO.js +10 -0
  109. package/dist/m/c-SE3SIY7Y.js +7 -0
  110. package/dist/m/{c-XX75FFNL.js → c-SGGGCIVD.js} +1 -1
  111. package/dist/m/c-SGH3UTF3.js +1 -0
  112. package/dist/m/c-SIM7Z3U7.js +1 -0
  113. package/dist/m/{c-X5LNHET5.js → c-SKMIR5HQ.js} +1 -1
  114. package/dist/m/{c-R27DWQKX.js → c-STRI73GQ.js} +1 -1
  115. package/dist/m/c-SUXHUL7U.js +1 -0
  116. package/dist/m/c-SXGEWPFC.js +10 -0
  117. package/dist/m/c-SXNSSHPP.js +1 -0
  118. package/dist/m/c-T3P3AXEW.js +1 -0
  119. package/dist/m/{c-PXLTUYZC.js → c-TFLRT5JX.js} +1 -1
  120. package/dist/m/{c-YCLU4UNT.js → c-TGIMBAWE.js} +1 -1
  121. package/dist/m/c-TGSHHFO6.js +1 -0
  122. package/dist/m/c-U4NTUVDZ.js +1 -0
  123. package/dist/m/c-UOOIF2AL.js +1 -0
  124. package/dist/m/{c-6TYGD5AB.js → c-UOUUI265.js} +1 -1
  125. package/dist/m/c-V2U3F4QX.js +1 -0
  126. package/dist/m/c-V2V5FKXX.js +1 -0
  127. package/dist/m/c-VBXRXTVS.js +26 -0
  128. package/dist/m/c-VHSMPITY.js +1 -0
  129. package/dist/m/c-VPXCXBDT.js +1 -0
  130. package/dist/m/c-WCG6AKA7.js +1 -0
  131. package/dist/m/c-WNOLMHI2.js +1 -0
  132. package/dist/m/{c-CRPIDGXC.js → c-X6MOYATI.js} +1 -1
  133. package/dist/m/{c-FQGHR32O.js → c-X75G43SZ.js} +1 -1
  134. package/dist/m/c-XB6EAIAR.js +13 -0
  135. package/dist/m/c-XMIDHEKF.js +1 -0
  136. package/dist/m/c-XUZMRQ7O.js +4 -0
  137. package/dist/m/{c-2Z22FCFK.js → c-XW5TIYYB.js} +1 -1
  138. package/dist/m/c-YDW4VCHP.js +1 -0
  139. package/dist/m/{c-QGAPG7OM.js → c-YLHTD3KX.js} +1 -1
  140. package/dist/m/c-YNIEIPBS.js +1 -0
  141. package/dist/m/c-Z4G2UK6T.js +4 -0
  142. package/dist/m/c-Z6MROEQ4.js +1 -0
  143. package/dist/m/c-ZFO5RW6G.js +2 -0
  144. package/dist/m/c-ZGCO4J3H.js +8 -0
  145. package/dist/m/c-ZKKAF4UT.js +3 -0
  146. package/dist/m/c-ZRGEN6QQ.js +4 -0
  147. package/dist/m/core/{admin-attrs-U7IJNYBG.js → admin-attrs-ONRWVXPK.js} +1 -1
  148. package/dist/m/core/{autosave-F5YQCKVL.js → autosave-TBKAAAFB.js} +1 -1
  149. package/dist/m/core/conflict-notice-FOEE6I7E.js +1 -0
  150. package/dist/m/core/draft-mode-4MCL3LYI.js +1 -0
  151. package/dist/m/core/{host-permission-6EVJGNPL.js → host-permission-X4BENZEU.js} +1 -1
  152. package/dist/m/core/{is-edit-mode-GCX6767E.js → is-edit-mode-FFHCQ5DR.js} +1 -1
  153. package/dist/m/core/{persist-CJXIL7D4.js → persist-6ILUR34S.js} +1 -1
  154. package/dist/m/core/{recovery-DRFZRJED.js → recovery-APWADU4K.js} +1 -1
  155. package/dist/m/core/save-6HT2LABE.js +1 -0
  156. package/dist/m/core/save-core-EVMNZQ4V.js +1 -0
  157. package/dist/m/core/site-edits-G4BOZMV4.js +1 -0
  158. package/dist/m/core/{unsaved-warning-HNQVTA2D.js → unsaved-warning-ITZDIZ2I.js} +1 -1
  159. package/dist/m/internals/{index-NRPNDTSA.js → index-HS3CQDFW.js} +1 -1
  160. package/dist/m/loader-GLLVDUZA.js +1 -0
  161. package/dist/m/manifest-50FF1C45.js +1 -0
  162. package/dist/m/manifest-edit-BE1A621D.js +1 -0
  163. package/dist/m/manifest.json +2072 -1811
  164. package/dist/m/page/editor-KAZDJNND.js +307 -0
  165. package/dist/m/plugins/ai-edit-LABECCSN.js +6 -0
  166. package/dist/m/plugins/{cms-H7DTMUQX.js → cms-T2HLI7JG.js} +16 -16
  167. package/dist/m/plugins/{demo-42BSYQ7T.js → demo-T7B4RIKL.js} +1 -1
  168. package/dist/m/plugins/{derive-QVI3LI3A.js → derive-6LPJOCAV.js} +1 -1
  169. package/dist/m/plugins/indicator-J4ABFARX.js +1 -0
  170. package/dist/m/plugins/{live-DCUBKXY4.js → live-PGBIEMGB.js} +1 -1
  171. package/dist/m/plugins/menu-YZL3X2AJ.js +1 -0
  172. package/dist/m/plugins/page-WOCNX2IN.js +2 -0
  173. package/dist/m/plugins/page-view-SJODOOGE.js +1 -0
  174. package/dist/m/plugins/{people-5XRNRP5U.js → people-C4TCWFOQ.js} +1 -1
  175. package/dist/m/plugins/{quickcrop-WWF5I422.js → quickcrop-X5MRIBAP.js} +2 -2
  176. package/dist/m/plugins/richclay-Y2NV44NA.js +549 -0
  177. package/dist/m/plugins/sortable-XCHQG65Z.js +1 -0
  178. package/dist/m/plugins/{source-6BKMK4PO.js → source-YK7MO23W.js} +1 -1
  179. package/dist/m/plugins/{undo-3JEYK5QY.js → undo-VLRBQNPN.js} +1 -1
  180. package/dist/m/plugins/upgrade-NNTJBSK2.js +1 -0
  181. package/dist/m/plugins/{upload-TTCQRIM5.js → upload-ZKKJV76X.js} +1 -1
  182. package/dist/m/plugins/visitor-drafts-RTZNONHT.js +20 -0
  183. package/dist/m/plugins/{visitor-drafts-owner-Z5B2OQBR.js → visitor-drafts-owner-LHUHFU3L.js} +1 -1
  184. package/dist/m/plugins/{visitor-menu-4LQMKLPP.js → visitor-menu-FY4KM2SW.js} +1 -1
  185. package/dist/m/plugins/wire-M2NR354P.js +1 -0
  186. package/dist/m/{reader-core-POFQCKRV.js → reader-core-T2S3JC4E.js} +2 -2
  187. package/dist/m/sync/live-sync-Z64YREWQ.js +1 -0
  188. package/dist/m/ui/index-MGTWOW2C.js +1 -0
  189. package/dist/m/vendor/{sap-core.vendor-OA5SKL7Q.js → sap-core.vendor-QAEPMETX.js} +1 -1
  190. package/dist/offline-manifest.json +1250 -1082
  191. package/dist/server/core.cjs +132 -109
  192. package/dist/server/page-compaction-worker.cjs +5 -5
  193. package/dist/server/rooms.mjs +3 -1
  194. package/docs/guide.md +89 -44
  195. package/entries/clay-data.js +1 -1
  196. package/entries/sap.js +1 -1
  197. package/package.json +5 -3
  198. package/src/blockeditor/themes/content.css +70 -0
  199. package/src/blockeditor/themes/light-1.css +431 -0
  200. package/src/blockeditor/themes/light-1.source.css +270 -0
  201. package/src/blockeditor/themes/light-2.css +211 -0
  202. package/src/blockeditor/themes/light-3.css +130 -0
  203. package/src/blockeditor/themes/light-4.css +299 -0
  204. package/src/core/conflict-notice.js +28 -0
  205. package/src/core/draft-mode.js +6 -0
  206. package/src/core/host-meta.js +6 -6
  207. package/src/core/is-edit-mode.js +9 -3
  208. package/src/core/mode-support.js +3 -0
  209. package/src/core/mode-switch.js +4 -4
  210. package/src/core/mode-transition.js +3 -1
  211. package/src/core/page-format-wire.js +1 -1
  212. package/src/core/page-format.js +8 -8
  213. package/src/core/save-accepted.js +6 -7
  214. package/src/core/save-core.js +52 -23
  215. package/src/core/save-failure-notice.js +37 -0
  216. package/src/core/save-feedback.js +6 -0
  217. package/src/core/save-replace.js +1 -1
  218. package/src/core/save-size.js +2 -2
  219. package/src/core/save.js +78 -15
  220. package/src/core/site-edits.js +166 -0
  221. package/src/core/unsaved-warning.js +2 -2
  222. package/src/core/upgrade-run.js +4 -4
  223. package/src/lib/ai-comment.js +23 -0
  224. package/src/lib/effect.js +8 -0
  225. package/src/lib/idb-error-code.js +20 -0
  226. package/src/live/fast-lane.js +10 -10
  227. package/src/live/local-lane.js +10 -10
  228. package/src/live/page-compaction.js +8 -8
  229. package/src/live/server-lane.js +1 -1
  230. package/src/loader-logic.js +48 -18
  231. package/src/loader.js +11 -6
  232. package/src/page/adopt-boot.js +1 -1
  233. package/src/page/adopt.js +2 -2
  234. package/src/page/ai-area.js +182 -0
  235. package/src/page/ai-preview.js +80 -0
  236. package/src/page/ai-surface.js +257 -0
  237. package/src/page/ai-tool.js +308 -145
  238. package/src/page/announce.js +2 -2
  239. package/src/page/awareness.js +18 -18
  240. package/src/page/blocks.js +35 -34
  241. package/src/page/blocksel.js +2 -2
  242. package/src/page/board-drag.js +11 -11
  243. package/src/page/board-face.js +1 -1
  244. package/src/page/board-panel.js +26 -26
  245. package/src/page/board-style.js +127 -127
  246. package/src/page/board-view.js +48 -48
  247. package/src/page/chart.js +29 -29
  248. package/src/page/checkpoint-proof.js +19 -19
  249. package/src/page/chrome.js +74 -65
  250. package/src/page/clipboard-shape.js +1 -1
  251. package/src/page/csv-import.js +4 -4
  252. package/src/page/drag.js +5 -5
  253. package/src/page/editor.js +301 -296
  254. package/src/page/export-html.js +8 -8
  255. package/src/page/features/checklist.js +36 -36
  256. package/src/page/features/export-html.js +19 -19
  257. package/src/page/features/export-markdown.js +10 -10
  258. package/src/page/features/import-markdown.js +13 -13
  259. package/src/page/features/media.js +50 -50
  260. package/src/page/features/table.js +17 -17
  261. package/src/page/features/title-cover.js +17 -17
  262. package/src/page/features/title-icon.js +7 -7
  263. package/src/page/features/toc.js +1 -1
  264. package/src/page/features/tool.js +42 -18
  265. package/src/page/format-command.js +4 -4
  266. package/src/page/format-gate.js +6 -6
  267. package/src/page/format-host.js +6 -6
  268. package/src/page/format-level.js +3 -3
  269. package/src/page/format-native-commands.js +2 -2
  270. package/src/page/highlight-slot.js +1 -1
  271. package/src/page/highlight.js +4 -4
  272. package/src/page/history-intent.js +2 -2
  273. package/src/page/image-actions.js +4 -4
  274. package/src/page/link.js +10 -10
  275. package/src/page/media.js +3 -3
  276. package/src/page/mention-picker.js +7 -7
  277. package/src/page/merge.js +1 -1
  278. package/src/page/placeholder.js +3 -3
  279. package/src/page/projection.js +11 -11
  280. package/src/page/reader-patch.js +2 -2
  281. package/src/page/recover.js +26 -26
  282. package/src/page/recovery-ui.js +28 -28
  283. package/src/page/rich/chart.js +1 -1
  284. package/src/page/rich/page-tool.js +21 -6
  285. package/src/page/rich/tool-runtime.js +3 -2
  286. package/src/page/save-projection.js +2 -2
  287. package/src/page/save-states.js +9 -9
  288. package/src/page/schema.js +9 -9
  289. package/src/page/table-style.js +21 -21
  290. package/src/page/toc-link.js +1 -1
  291. package/src/page/tool-capability.js +8 -3
  292. package/src/page/tool-transport.js +52 -0
  293. package/src/page/tool-views.js +4 -0
  294. package/src/page/tools.js +6 -1
  295. package/src/page/view.js +6 -6
  296. package/src/page/ybind/binding.js +1 -1
  297. package/src/page/ybind/conversion.js +1 -1
  298. package/src/page/ybind/layout.js +4 -4
  299. package/src/page/ybind/migrate.js +1 -1
  300. package/src/page/ybind/origins.js +2 -2
  301. package/src/page/ybind/provider.js +2 -2
  302. package/src/page/ybind/recovery-proof.js +3 -3
  303. package/src/page/ybind/recovery-replay.js +2 -2
  304. package/src/plugins/ai-edit/picker.js +235 -0
  305. package/src/plugins/ai-edit.js +220 -34
  306. package/src/plugins/indicator-chip.js +39 -34
  307. package/src/plugins/indicator.js +13 -4
  308. package/src/plugins/live.js +21 -13
  309. package/src/plugins/menu/actions.js +89 -17
  310. package/src/plugins/menu/seo.js +32 -6
  311. package/src/plugins/menu/shell-style.js +26 -7
  312. package/src/plugins/menu/shell.js +12 -7
  313. package/src/plugins/menu/variants.js +6 -10
  314. package/src/plugins/menu/visitor.js +44 -34
  315. package/src/plugins/menu.js +3 -2
  316. package/src/plugins/page-view.js +2 -2
  317. package/src/plugins/page.js +257 -98
  318. package/src/plugins/sortable.js +7 -0
  319. package/src/plugins/upgrade.js +2 -1
  320. package/src/plugins/visitor-drafts/commit.js +303 -0
  321. package/src/plugins/visitor-drafts/idb.js +221 -0
  322. package/src/plugins/visitor-drafts/intro-style.js +1 -1
  323. package/src/plugins/visitor-drafts/intro.js +8 -25
  324. package/src/plugins/visitor-drafts/prompt.js +30 -4
  325. package/src/plugins/visitor-drafts/session.js +345 -0
  326. package/src/plugins/visitor-drafts/store.js +351 -32
  327. package/src/plugins/visitor-drafts.js +158 -27
  328. package/src/server/checkpoint/index.js +8 -8
  329. package/src/server/rooms/index.js +2 -2
  330. package/src/server/rooms/live-data-rooms.js +35 -35
  331. package/src/server/rooms/live-page-history.js +2 -2
  332. package/src/server/rooms/page-admission.js +34 -6
  333. package/src/server/rooms/page-conversion.js +1 -1
  334. package/src/server/rooms/page-lineage.js +4 -4
  335. package/src/server/rooms/page-rebase.js +4 -4
  336. package/src/server/rooms/page-room-conformance.js +14 -14
  337. package/src/server/rooms/page-state.js +2 -2
  338. package/src/server/rooms/page-write-admission.js +7 -7
  339. package/src/server/rooms/page-write-guard.js +3 -3
  340. package/src/sync/data-guard-notice.js +265 -0
  341. package/src/sync/live-sync.js +54 -11
  342. package/src/sync/page-relay.js +4 -4
  343. package/src/sync/presence-store.js +1 -1
  344. package/src/sync/presence-target.js +3 -3
  345. package/src/sync/presence.js +1 -1
  346. package/src/sync/stream.js +54 -13
  347. package/src/sync/wire-profile.js +8 -3
  348. package/src/ui/bevel.js +1 -1
  349. package/src/ui/index.js +9 -10
  350. package/src/ui/skins/cms.js +1 -1
  351. package/src/ui/skins/quickcrop.js +1 -1
  352. package/src/ui/skins/richclay.js +2 -2
  353. package/src/ui/toast-lazy.js +12 -3
  354. package/src/ui/toast.js +9 -3
  355. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  356. package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
  357. package/src/vendor/hypercms.vendor.js +16 -16
  358. package/src/vendor/richclay.vendor.js +48 -20
  359. package/src/vendor/sap-core.vendor.js +1 -1
  360. package/tool-generation/context.md +44 -0
  361. package/tool-generation/system.md +23 -0
  362. package/dist/m/c-24PMUJIV.js +0 -2
  363. package/dist/m/c-2AYVYEI4.js +0 -1
  364. package/dist/m/c-2AZDZWCK.js +0 -1
  365. package/dist/m/c-2L6O5PDS.js +0 -6
  366. package/dist/m/c-2RYOGUQM.js +0 -2
  367. package/dist/m/c-2ZVOEU4M.js +0 -1
  368. package/dist/m/c-37NAONZA.js +0 -1
  369. package/dist/m/c-3GJ4SDE2.js +0 -1
  370. package/dist/m/c-3O7OBDCM.js +0 -1
  371. package/dist/m/c-5OJ7YF2N.js +0 -1
  372. package/dist/m/c-5WMXEKFH.js +0 -135
  373. package/dist/m/c-6RDOKLN7.js +0 -1
  374. package/dist/m/c-A4IP6MBC.js +0 -1
  375. package/dist/m/c-A6L3FMUS.js +0 -1
  376. package/dist/m/c-ATKAOH23.js +0 -1
  377. package/dist/m/c-B6O7AJYG.js +0 -32
  378. package/dist/m/c-BCGWJYXW.js +0 -1
  379. package/dist/m/c-BGKAT2FL.js +0 -7
  380. package/dist/m/c-BYTKL6UX.js +0 -1
  381. package/dist/m/c-DHBRA6DL.js +0 -1
  382. package/dist/m/c-DTMZ3ZHI.js +0 -1
  383. package/dist/m/c-E3ZXARWA.js +0 -1
  384. package/dist/m/c-EK63G3CG.js +0 -1
  385. package/dist/m/c-ELYIIYRS.js +0 -1
  386. package/dist/m/c-EXKHPOVC.js +0 -3
  387. package/dist/m/c-FCSZ3DC5.js +0 -20
  388. package/dist/m/c-FTWGYI5E.js +0 -1
  389. package/dist/m/c-G4PWUDD5.js +0 -1
  390. package/dist/m/c-GBKB5WDL.js +0 -2
  391. package/dist/m/c-GBTVCJXC.js +0 -1
  392. package/dist/m/c-GC2HICFJ.js +0 -1
  393. package/dist/m/c-GRWMD6O5.js +0 -1
  394. package/dist/m/c-HCNWJE4A.js +0 -4
  395. package/dist/m/c-HG2C77BM.js +0 -4
  396. package/dist/m/c-JGHEACGK.js +0 -1
  397. package/dist/m/c-JGLX6TBK.js +0 -1
  398. package/dist/m/c-JNRKHYWO.js +0 -1
  399. package/dist/m/c-KBS3UV6B.js +0 -1
  400. package/dist/m/c-KQ7E3PYY.js +0 -1
  401. package/dist/m/c-L3F5ZLIV.js +0 -1
  402. package/dist/m/c-L4KMT7FO.js +0 -6
  403. package/dist/m/c-L5VFATT6.js +0 -1
  404. package/dist/m/c-LCYPMULG.js +0 -1
  405. package/dist/m/c-LIGQYZ7M.js +0 -6
  406. package/dist/m/c-LKIWEPAV.js +0 -1
  407. package/dist/m/c-MPSKIU2I.js +0 -1
  408. package/dist/m/c-N7JPUPGC.js +0 -1
  409. package/dist/m/c-NK33MMYF.js +0 -1
  410. package/dist/m/c-NQLWOHLH.js +0 -10
  411. package/dist/m/c-NTHUTABN.js +0 -1
  412. package/dist/m/c-NWX3MFT4.js +0 -1
  413. package/dist/m/c-OKXI52N7.js +0 -1
  414. package/dist/m/c-OPONJ2UK.js +0 -128
  415. package/dist/m/c-OWQWOGYI.js +0 -1
  416. package/dist/m/c-P6Q2ZTFQ.js +0 -1
  417. package/dist/m/c-QEN6HZCA.js +0 -1
  418. package/dist/m/c-QIXT3Q7C.js +0 -28
  419. package/dist/m/c-QM5LXRDT.js +0 -9
  420. package/dist/m/c-QYYUVLVG.js +0 -1
  421. package/dist/m/c-RQRMPZXD.js +0 -1
  422. package/dist/m/c-SBCBXKOZ.js +0 -26
  423. package/dist/m/c-SKF5HL4I.js +0 -4
  424. package/dist/m/c-SRPC3VBE.js +0 -1
  425. package/dist/m/c-TANBH7FI.js +0 -1
  426. package/dist/m/c-THGQQDYY.js +0 -1
  427. package/dist/m/c-UD5JZTDL.js +0 -1
  428. package/dist/m/c-UDGEFWNU.js +0 -1
  429. package/dist/m/c-UGNV5MFB.js +0 -1
  430. package/dist/m/c-VIRFF5HG.js +0 -8
  431. package/dist/m/c-VJYEVRYK.js +0 -1
  432. package/dist/m/c-VQCEOGKH.js +0 -13
  433. package/dist/m/c-VYJ7O4RI.js +0 -34
  434. package/dist/m/c-W7IPVCAA.js +0 -1
  435. package/dist/m/c-WBGWGOM7.js +0 -1
  436. package/dist/m/c-WJ5DRBHT.js +0 -72
  437. package/dist/m/c-X5WC4PX5.js +0 -1
  438. package/dist/m/c-XCFQEWW6.js +0 -3
  439. package/dist/m/c-XGH4GS2T.js +0 -1
  440. package/dist/m/c-XLQ3BDMM.js +0 -1
  441. package/dist/m/c-ZQXNHZZT.js +0 -72
  442. package/dist/m/core/conflict-notice-42H4ASLA.js +0 -1
  443. package/dist/m/core/draft-mode-WSQAT67S.js +0 -1
  444. package/dist/m/core/save-ESPSQPLV.js +0 -1
  445. package/dist/m/core/save-core-6SQHOR6W.js +0 -1
  446. package/dist/m/loader-FBDE2UAB.js +0 -1
  447. package/dist/m/manifest-1D2F5CA5.js +0 -1
  448. package/dist/m/manifest-edit-F7CC5CFD.js +0 -1
  449. package/dist/m/page/editor-OSKOW6RS.js +0 -304
  450. package/dist/m/plugins/ai-edit-VV4QTINH.js +0 -6
  451. package/dist/m/plugins/indicator-WVPGMOZ7.js +0 -1
  452. package/dist/m/plugins/menu-Q4MFFQ6E.js +0 -1
  453. package/dist/m/plugins/page-OAWUJQEL.js +0 -2
  454. package/dist/m/plugins/page-view-DZ26ZDDQ.js +0 -1
  455. package/dist/m/plugins/richclay-PGOOJ4OK.js +0 -517
  456. package/dist/m/plugins/sortable-266HY6TR.js +0 -1
  457. package/dist/m/plugins/upgrade-BNZVFHMZ.js +0 -1
  458. package/dist/m/plugins/visitor-drafts-2AUYKAIJ.js +0 -20
  459. package/dist/m/plugins/wire-W2EV36KZ.js +0 -1
  460. package/dist/m/sync/live-sync-XGUPHI3I.js +0 -1
  461. package/dist/m/ui/index-VGYAKAKI.js +0 -1
  462. package/dist/page/themes/light-1.css +0 -431
  463. package/src/page/themes/content.css +0 -70
  464. package/src/page/themes/light-1.css +0 -431
  465. package/src/page/themes/light-1.source.css +0 -270
  466. package/src/page/themes/light-2.css +0 -211
  467. package/src/page/themes/light-3.css +0 -130
  468. package/src/page/themes/light-4.css +0 -299
  469. /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
  470. /package/dist/m/plugins/{quickcrop-WWF5I422.js.LEGAL.txt → quickcrop-X5MRIBAP.js.LEGAL.txt} +0 -0
  471. /package/dist/m/plugins/{richclay-PGOOJ4OK.js.LEGAL.txt → richclay-Y2NV44NA.js.LEGAL.txt} +0 -0
@@ -1,188 +1,351 @@
1
+ // /ai tools as contract A targets: the page's AI surface builds one of these and the
2
+ // shared panel in ai-edit.js drives it. Making a tool and revising one differ only in
3
+ // the request's target and current revision, and in what Keep writes.
1
4
  import { state } from "../vendor/prosemirror.vendor.js";
2
5
  import { schema } from "./schema.js";
3
6
  import { asStep } from "./blocksel.js";
4
7
  import { keyBetween, newId } from "./model.js";
5
8
  import { createToolClient } from "./rich/page-tool.js";
6
9
  import { createToolHost } from "./tools.js";
7
- import { toolContext } from "./tool-data.js";
10
+ import { tablesFromDoc, toolContext } from "./tool-data.js";
11
+ import { showPreview, showWidget, clearPreview } from "./ai-preview.js";
12
+ import { validateArtifact, validateState } from "./rich/tool-runtime.js";
13
+ import { createToolTransport } from "./tool-transport.js";
14
+ import { toolBlockViews } from "./tool-views.js";
8
15
 
9
16
  const { Selection } = state;
10
17
 
18
+ const GONE = "This page is not open for editing any more.";
19
+ const NO_WIRE = "AI tool making isn't available on this page right now.";
20
+ const NO_STORE = "This document can't save tool data.";
21
+ const MOVED = "The place for this tool moved. Close this and run /ai where you want the tool.";
22
+ const STALE_REVISION = "This tool changed while you were revising, so nothing was replaced. Ask again.";
23
+ const BAD_ARTIFACT = "AI did not make a usable tool. Try again.";
24
+ const BAD_STATE = "The tool data from AI is too large or not usable. Try again.";
25
+ const CHANGED = "This tool's data changed while AI was working, so it was not replaced. Ask again to include the latest data.";
26
+ const SAVE_FAILED = "The tool could not be saved, so it was not added. Try again.";
27
+ const UNSAVED = "Your tool is in the document, but saving failed. It will be saved with your next edit.";
28
+
11
29
  function findBlock(doc, id) {
12
30
  let found = null;
13
31
  doc.forEach((node, at, index) => { if (node.attrs.id === id) found = { node, at, index }; });
14
32
  return found;
15
33
  }
16
34
 
35
+ // The block the /ai was typed in and the text of that query. Keep uses it to place the
36
+ // tool and to remove exactly the slash text, never a caret that moved meanwhile.
37
+ function bookmarkIn(st, remove) {
38
+ const $head = st.selection.$head;
39
+ const index = Math.min(Math.max($head.index(0), 0), st.doc.childCount - 1);
40
+ const host = st.doc.child(index);
41
+ return {
42
+ hostId: host?.attrs.id ?? null,
43
+ slashText: remove ? st.doc.textBetween(remove.from, remove.to) : null,
44
+ slashOffset: remove && $head.depth ? remove.from - $head.before(1) - 1 : null,
45
+ };
46
+ }
47
+
48
+ // Where the tool goes: over an empty or slash-only block, else in a new block after it.
49
+ function placeIn(st, bookmark) {
50
+ const found = bookmark?.hostId ? findBlock(st.doc, bookmark.hostId) : null;
51
+ if (!found) return null;
52
+ const next = st.doc.maybeChild(found.index + 1);
53
+ const replace = found.node.isTextblock && (found.node.content.size === 0 || found.node.textContent === bookmark.slashText);
54
+ return {
55
+ ...found,
56
+ replace,
57
+ pos: replace ? found.node.attrs.pos : keyBetween(found.node.attrs.pos, next?.attrs.pos ?? null),
58
+ insertAt: replace ? found.at : found.at + found.node.nodeSize,
59
+ };
60
+ }
61
+
17
62
  function insertCandidate(view, candidate, bookmark) {
18
63
  const st = view.state;
19
- const found = findBlock(st.doc, bookmark?.hostId) ?? (() => {
20
- const index = Math.min(st.selection.$head.index(0), st.doc.childCount - 1);
21
- let at = 0;
22
- for (let i = 0; i < index; i++) at += st.doc.child(i).nodeSize;
23
- return { node: st.doc.child(index), at, index };
24
- })();
25
- const { node: current, at, index } = found;
26
- const next = st.doc.maybeChild(index + 1);
27
- const replace = current.isTextblock && (current.content.size === 0 || bookmark?.hostId === current.attrs.id && current.textContent === bookmark.slashText);
28
- const pos = replace ? current.attrs.pos : keyBetween(current.attrs.pos, next?.attrs.pos ?? null);
64
+ const place = placeIn(st, bookmark);
65
+ if (!place) return false;
66
+ const { node: current, at, insertAt, replace } = place;
29
67
  const tool = schema.nodes.tool.create({
30
- id: candidate.id, pos, parent: current.attrs.parent ?? null,
68
+ id: candidate.id, pos: place.pos, parent: current.attrs.parent ?? null,
31
69
  artifact: candidate.artifact, rev: candidate.rev, state: candidate.state,
32
70
  });
33
- const place = replace ? at : at + current.nodeSize;
34
71
  const tr = st.tr;
35
72
  if (!replace && bookmark?.hostId === current.attrs.id && bookmark.slashText && bookmark.slashOffset >= 0) {
36
73
  const from = at + 1 + bookmark.slashOffset;
37
74
  const to = from + bookmark.slashText.length;
38
75
  if (to <= at + current.nodeSize - 1 && st.doc.textBetween(from, to) === bookmark.slashText) tr.delete(from, to);
39
76
  }
40
- const mapped = tr.mapping.map(place);
77
+ const mapped = tr.mapping.map(insertAt);
41
78
  tr.replaceWith(mapped, replace ? tr.mapping.map(at + current.nodeSize) : mapped, tool);
42
79
  tr.setSelection(Selection.near(tr.doc.resolve(Math.min(mapped + tool.nodeSize, tr.doc.content.size))));
43
80
  view.dispatch(asStep(tr).scrollIntoView());
81
+ return true;
44
82
  }
45
83
 
46
- export function createAiTool({ getView, store, transport, acceptState = () => globalThis.clay?.save?.flush?.(), newId: allocate = newId }) {
47
- const client = transport ? createToolClient({ transport, newId: () => allocate("request") }) : null;
48
- let panel = null;
49
- let generation = 0;
50
-
51
- const close = () => {
52
- generation++;
53
- if (!panel) return;
54
- client?.discard(panel.pending);
55
- panel.preview?.dispose();
56
- panel.previewHost?.dispose();
57
- panel.element.remove();
58
- panel = null;
59
- };
84
+ const emptyRect = () => ({ x: 0, y: 0, left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 });
60
85
 
61
- const open = ({ target = null, remove = null } = {}) => {
62
- close();
86
+ export function createToolTargets({
87
+ getView,
88
+ store = () => null,
89
+ acceptState = () => globalThis.clay?.save?.flush?.(),
90
+ newId: allocate = newId,
91
+ transport = createToolTransport(),
92
+ } = {}) {
93
+ const viewNow = () => {
63
94
  const view = getView();
64
- if (!view) return;
65
- const selection = view.state.selection;
66
- const host = view.state.doc.child(Math.min(selection.$head.index(0), view.state.doc.childCount - 1));
67
- const bookmark = {
68
- hostId: host?.attrs.id,
69
- slashText: remove ? view.state.doc.textBetween(remove.from, remove.to) : null,
70
- slashOffset: remove && selection.$head.depth ? remove.from - selection.$head.before(1) - 1 : null,
71
- };
72
- const doc = view.dom.ownerDocument;
73
- const element = doc.createElement("section");
74
- element.className = "page-ai-panel";
75
- element.contentEditable = "false";
76
- element.setAttribute("role", "dialog");
77
- element.setAttribute("aria-label", target ? "Revise tool" : "Make a tool with AI");
78
- const input = doc.createElement("textarea");
79
- input.setAttribute("aria-label", "Describe the tool");
80
- input.placeholder = target ? "What should change?" : "Describe the tool you want";
81
- const status = doc.createElement("p");
82
- status.className = "page-ai-status";
83
- const previewRoot = doc.createElement("div");
84
- previewRoot.className = "page-ai-preview";
85
- const actions = doc.createElement("div");
86
- actions.className = "page-ai-actions";
87
- element.append(input, status, previewRoot, actions);
88
- view.dom.parentElement.append(element);
89
- panel = { element, input, status, previewRoot, actions, target, bookmark, candidate: null, pending: null, preview: null, previewHost: null, kept: false, busy: false };
90
-
91
- const button = (label, run) => {
92
- const el = doc.createElement("button");
93
- el.type = "button";
94
- el.textContent = label;
95
- el.addEventListener("click", run);
96
- actions.append(el);
97
- };
98
- const showPrompt = () => {
99
- if (!panel) return;
100
- panel.preview?.dispose();
101
- panel.previewHost?.dispose();
102
- panel.preview = null;
103
- panel.previewHost = null;
104
- panel.previewRoot.replaceChildren();
105
- panel.input.hidden = false;
106
- panel.actions.replaceChildren();
107
- button("Generate", generate);
108
- button("Cancel", close);
109
- panel.input.focus();
95
+ return view && !view.isDestroyed ? view : null;
96
+ };
97
+ const storeNow = () => (typeof store === "function" ? store() : store);
98
+
99
+ const liveState = (id, rev) => {
100
+ const value = storeNow()?.data?.[id]?.[rev];
101
+ return value === undefined ? {} : value;
102
+ };
103
+
104
+ const targets = new Map();
105
+
106
+ const canonical = (value) => {
107
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
108
+ if (value && typeof value === "object") {
109
+ const keys = Object.keys(value).sort();
110
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
111
+ }
112
+ return JSON.stringify(value) ?? "null";
113
+ };
114
+
115
+ function toolTarget(toolId = null, remove = null) {
116
+ const opened = viewNow();
117
+ const bookmark = toolId || !opened ? null : bookmarkIn(opened.state, remove);
118
+ let onStatus = null;
119
+ const client = createToolClient({
120
+ transport: {
121
+ request: (request) => transport.request(request, { onStatus: (status) => onStatus?.(status) }),
122
+ cancel: (id) => transport.cancel(id),
123
+ },
124
+ });
125
+ let pending = null;
126
+ let candidate = null;
127
+ let preview = null;
128
+ let kept = false;
129
+ let revision = null;
130
+ let captured = null;
131
+
132
+ const disposePreview = () => {
133
+ preview?.host?.dispose();
134
+ preview = null;
110
135
  };
111
- const showCandidate = () => {
112
- const p = panel;
113
- p.input.hidden = true;
114
- p.status.textContent = "";
115
- p.previewHost = createToolHost({ mode: "view" });
116
- p.preview = p.previewHost.mount(p.previewRoot, { id: "preview", rev: "preview", artifact: p.candidate.artifact, seed: p.candidate.state });
117
- p.actions.replaceChildren();
118
- button("Keep", keep);
119
- button("Revise", showPrompt);
120
- button("Discard", close);
136
+
137
+ // The block that runs this tool, if this editor mounts it, so a revision can pause
138
+ // it while the request and its preview are up.
139
+ const blockNow = () => {
140
+ const view = viewNow();
141
+ return view && toolId ? toolBlockViews.get(view)?.get(toolId) ?? null : null;
121
142
  };
122
- const generate = async () => {
123
- const p = panel;
124
- if (!p || p.busy || !p.input.value.trim()) return;
125
- if (!client) { p.status.textContent = "Tool generation is unavailable"; return; }
126
- p.busy = true;
127
- p.status.textContent = "Making your tool…";
128
- p.actions.replaceChildren();
129
- button("Cancel", close);
130
- const turn = ++generation;
131
- const candidate = client.request(p.input.value.trim(), toolContext(view.state.doc, doc.title), p.target);
132
- p.pending = candidate;
133
- try {
134
- const result = await client.wait(candidate);
135
- if (panel !== p || turn !== generation) return;
136
- p.candidate = { artifact: result.artifact, state: result.state, id: null, rev: null };
137
- showCandidate();
138
- } catch (error) {
139
- if (panel !== p || turn !== generation) return;
140
- p.status.textContent = error.message;
141
- p.actions.replaceChildren();
142
- button("Generate", generate);
143
- button("Cancel", close);
144
- } finally { p.busy = false; }
143
+
144
+ const suspendTool = () => blockNow()?.suspend();
145
+ const resumeTool = () => blockNow()?.resume();
146
+
147
+ // Every way Keep can fail leaves the same trace: no proposal on screen, the block
148
+ // and its state as they were, and an editor that answers again.
149
+ const failKeep = (message) => {
150
+ kept = false;
151
+ const view = viewNow();
152
+ disposePreview();
153
+ if (view) clearPreview(view);
154
+ resumeTool();
155
+ return { status: "failed", message };
145
156
  };
146
- const keep = async () => {
147
- const p = panel;
148
- if (!p?.candidate || p.busy || p.kept) return;
149
- p.busy = true;
150
- const candidate = p.candidate;
151
- candidate.id ??= p.target?.toolId ?? allocate("b");
152
- candidate.rev ??= allocate("rev");
153
- try {
154
- if (!store) throw new Error("This document can't save tool state");
155
- await store.set([candidate.id, candidate.rev], candidate.state);
156
- const accepted = await acceptState();
157
+
158
+ const target = {
159
+ aiTarget: true,
160
+ label: toolId ? "This tool" : "New tool",
161
+ ref: toolId ? [toolId] : [],
162
+ placeholder: toolId ? "What should change?" : "Describe the tool",
163
+ get isConnected() {
164
+ const view = viewNow();
165
+ if (!view) return false;
166
+ return toolId ? !!findBlock(view.state.doc, toolId) : true;
167
+ },
168
+ getBoundingClientRect() {
169
+ const view = viewNow();
170
+ if (!view) return emptyRect();
171
+ if (preview) return preview.dom.getBoundingClientRect();
172
+ const id = toolId ?? bookmark?.hostId;
173
+ const found = id ? findBlock(view.state.doc, id) : null;
174
+ const node = found ? view.nodeDOM(found.at) : null;
175
+ return node && typeof node.getBoundingClientRect === "function" ? node.getBoundingClientRect() : emptyRect();
176
+ },
177
+ async start(comment) {
178
+ candidate = null;
179
+ kept = false;
180
+ const view = viewNow();
181
+ if (!view) return { status: "refused", code: "gone", message: GONE };
182
+ // Read here, not imported: a page with exclude=wire must not pull the wire in.
183
+ if (!globalThis.window?.clay?.wire) return { status: "refused", code: "no-wire", message: NO_WIRE };
184
+ let payloadTarget = null;
185
+ let current = null;
186
+ if (toolId) {
187
+ const found = findBlock(view.state.doc, toolId);
188
+ if (found?.node.type !== schema.nodes.tool) return { status: "refused", code: "gone", message: GONE };
189
+ revision = found.node.attrs.rev;
190
+ payloadTarget = { toolId, rev: revision };
191
+ // The tool must not write to its own data while the request runs or its
192
+ // preview is shown, so it pauses and the snapshot is taken from its saved data.
193
+ suspendTool();
194
+ current = { artifact: found.node.attrs.artifact, state: liveState(toolId, revision) };
195
+ captured = canonical(current.state);
196
+ }
197
+ pending = client.request(comment, toolContext(view.state.doc, view.dom.ownerDocument.title), payloadTarget, current);
198
+ return {
199
+ status: "ok",
200
+ id: pending.requestId,
201
+ send: (hooks = {}) => {
202
+ onStatus = typeof hooks.onStatus === "function" ? hooks.onStatus : null;
203
+ const done = client.wait(pending).then(
204
+ (result) => {
205
+ candidate = { id: toolId, rev: null, artifact: result.artifact, state: result.state };
206
+ return { state: "done", result };
207
+ },
208
+ (error) => {
209
+ resumeTool();
210
+ return { state: "error", error: error?.message || error?.code || "the helper reported an error", code: error?.code ?? null };
211
+ },
212
+ );
213
+ return { done, cancel: () => { resumeTool(); client.discard(pending); } };
214
+ },
215
+ };
216
+ },
217
+ async propose(result) {
218
+ const view = viewNow();
219
+ if (!view || !result) return { status: "refused", message: GONE };
220
+ const artifact = validateArtifact(result.artifact);
221
+ if (!artifact.ok) return { status: "refused", message: BAD_ARTIFACT };
222
+ const held = validateState(result.state);
223
+ if (!held.ok) return { status: "refused", message: BAD_STATE };
224
+ candidate = { id: toolId, rev: null, artifact: result.artifact, state: result.state };
225
+ const mounted = mountPreview(view);
226
+ if (mounted.status !== "ready") candidate = null;
227
+ return mounted;
228
+ },
229
+ async keep() {
230
+ if (!candidate || kept) return failKeep(STALE_REVISION);
231
+ const view = viewNow();
232
+ if (!view) return failKeep(GONE);
233
+ const tools = storeNow();
234
+ if (!tools) return failKeep(NO_STORE);
235
+ const liveChanged = () => toolId && captured !== null && canonical(liveState(toolId, revision)) !== captured;
236
+ if (liveChanged()) return failKeep(CHANGED);
237
+ kept = true;
238
+ const id = candidate.id ?? allocate("b");
239
+ const rev = allocate("rev");
240
+ const edit = { ...candidate, id, rev };
241
+ // Two stages: the state has to be accepted first, and only then does the block
242
+ // appear, so a refused state leaves no block and one undo item.
243
+ try {
244
+ await tools.set([id, rev], edit.state);
245
+ } catch (error) {
246
+ return failKeep(error?.message ?? NO_STORE);
247
+ }
248
+ let accepted = null;
249
+ try {
250
+ accepted = await acceptState();
251
+ } catch {
252
+ return failKeep(SAVE_FAILED);
253
+ }
157
254
  if (accepted?.ok !== true && !["saved", "clean"].includes(accepted?.state)) {
158
- throw new Error(accepted?.msg ?? accepted?.message ?? "Tool state was not saved");
255
+ return failKeep(accepted?.msg ?? accepted?.message ?? SAVE_FAILED);
159
256
  }
160
- if (panel !== p || p.kept) return;
161
- if (p.target) {
162
- const found = findBlock(view.state.doc, p.target.toolId);
163
- if (found?.node.type !== schema.nodes.tool || found.node.attrs.rev !== p.target.rev) {
164
- p.status.textContent = "This tool changed while you were revising. Keep as a new tool, or discard.";
165
- p.target = null;
166
- candidate.id = null;
167
- candidate.rev = null;
168
- p.actions.replaceChildren();
169
- button("Keep as new tool", keep);
170
- button("Discard", close);
171
- return;
172
- }
257
+ if (toolId) {
258
+ const found = findBlock(view.state.doc, toolId);
259
+ if (found?.node.type !== schema.nodes.tool || found.node.attrs.rev !== revision) return failKeep(STALE_REVISION);
260
+ } else if (!placeIn(view.state, bookmark)) {
261
+ return failKeep(MOVED);
262
+ }
263
+ if (liveChanged()) return failKeep(CHANGED);
264
+ disposePreview();
265
+ clearPreview(view);
266
+ if (toolId) {
267
+ const found = findBlock(view.state.doc, toolId);
173
268
  view.dispatch(asStep(view.state.tr.setNodeMarkup(found.at, null, {
174
- ...found.node.attrs, artifact: candidate.artifact, rev: candidate.rev,
269
+ ...found.node.attrs, artifact: edit.artifact, rev: edit.rev,
175
270
  })));
176
- } else insertCandidate(view, candidate, p.bookmark);
177
- p.kept = true;
178
- close();
179
- } catch (error) {
180
- if (panel === p) p.status.textContent = error.message;
181
- } finally { p.busy = false; }
271
+ } else if (!insertCandidate(view, edit, bookmark)) {
272
+ return failKeep(MOVED);
273
+ }
274
+ // The block is in the document now: a failed save neither undoes it nor asks the
275
+ // model again, it retries the save alone, once.
276
+ let saved = false;
277
+ for (let attempt = 0; attempt < 2 && !saved; attempt++) {
278
+ try {
279
+ const result = await acceptState();
280
+ saved = result?.ok !== false;
281
+ } catch {
282
+ saved = false;
283
+ }
284
+ }
285
+ if (!saved) return { status: "failed", message: UNSAVED };
286
+ targets.delete(toolId ?? "new");
287
+ return { status: "kept" };
288
+ },
289
+ discard() {
290
+ disposePreview();
291
+ const view = viewNow();
292
+ if (view) clearPreview(view);
293
+ client.discard(pending);
294
+ resumeTool();
295
+ targets.delete(toolId ?? "new");
296
+ },
182
297
  };
183
- showPrompt();
184
- return element;
185
- };
186
298
 
187
- return { open, dispose: close };
299
+ function mountPreview(view) {
300
+ disposePreview();
301
+ const host = createToolHost({
302
+ mode: "view",
303
+ tables: () => tablesFromDoc(view.state.doc).map(({ id, cols, rows }) => ({ id, cols, rows })),
304
+ });
305
+ const dom = view.dom.ownerDocument.createElement("div");
306
+ dom.className = "blockeditor-ai-preview";
307
+ let mounted = null;
308
+ try {
309
+ mounted = host.mount(dom, { id: "preview", rev: "preview", artifact: candidate.artifact, seed: candidate.state });
310
+ } catch (error) {
311
+ host.dispose();
312
+ return { status: "refused", message: BAD_ARTIFACT };
313
+ }
314
+ if (mounted.failed) {
315
+ host.dispose();
316
+ return { status: "refused", message: BAD_ARTIFACT };
317
+ }
318
+ preview = { host, dom };
319
+ // A revision covers the block it replaces; a new tool stands at its bookmark.
320
+ if (toolId) {
321
+ showPreview(view, { ids: [toolId], dom });
322
+ return { status: "ready" };
323
+ }
324
+ const place = placeIn(view.state, bookmark);
325
+ if (!place) {
326
+ disposePreview();
327
+ return { status: "refused", message: MOVED };
328
+ }
329
+ if (place.replace) showPreview(view, { ids: [place.node.attrs.id], dom });
330
+ else showWidget(view, { at: place.insertAt, dom });
331
+ return { status: "ready" };
332
+ }
333
+
334
+ if (toolId) targets.set(toolId, target);
335
+ return target;
336
+ }
337
+
338
+ return {
339
+ newTool(remove = null) {
340
+ return toolTarget(null, remove);
341
+ },
342
+ // A revision target follows the block it names: the same tool asked about twice is
343
+ // the same target, so its draft survives a closed panel.
344
+ reviseTool(toolId) {
345
+ const known = targets.get(toolId);
346
+ if (known?.isConnected) return known;
347
+ targets.delete(toolId);
348
+ return toolTarget(toolId);
349
+ },
350
+ };
188
351
  }
@@ -4,7 +4,7 @@ export function announcementPlugin() {
4
4
  return new state.Plugin({
5
5
  view(view) {
6
6
  const live = view.dom.ownerDocument.createElement("div");
7
- live.className = "page-live";
7
+ live.className = "blockeditor-live";
8
8
  live.setAttribute("role", "status");
9
9
  live.setAttribute("aria-live", "polite");
10
10
  view.dom.parentElement.appendChild(live);
@@ -14,7 +14,7 @@ export function announcementPlugin() {
14
14
  }
15
15
 
16
16
  export function announce(view, text) {
17
- const region = view.dom.parentElement?.querySelector(".page-live");
17
+ const region = view.dom.parentElement?.querySelector(".blockeditor-live");
18
18
  if (!region) return;
19
19
  region.textContent = "";
20
20
  region.textContent = text;
@@ -18,16 +18,16 @@ import { styleWithLegacyId } from "../lib/style.js";
18
18
  const { Plugin, PluginKey } = state;
19
19
  const { Decoration, DecorationSet } = pmView;
20
20
 
21
- export const awarenessKey = new PluginKey("page-awareness");
21
+ export const awarenessKey = new PluginKey("blockeditor-awareness");
22
22
 
23
- const STYLE_ID = "page-awareness-style";
23
+ const STYLE_ID = "blockeditor-awareness-style";
24
24
  const STYLE_CSS = `
25
- .page-remote-selection { background-color: color-mix(in srgb, var(--page-remote-color, currentColor) 22%, transparent); }
26
- .page-remote-caret { position: relative; display: inline-block; width: 0; height: 1.1em; vertical-align: text-bottom; }
27
- .page-remote-caret::before { content: ""; position: absolute; inset-inline-start: -1px; top: 0; bottom: 0; width: 2px; background: var(--page-remote-color, currentColor); }
28
- .page-remote-name { position: absolute; inset-inline-start: -1px; bottom: 100%; padding: 1px 4px; border-radius: 4px; background: var(--page-remote-color, currentColor); color: #fff; font-size: 10px; line-height: 1.4; white-space: nowrap; pointer-events: none; }
29
- .page-remote-cell { outline: 2px solid var(--page-remote-color, currentColor); outline-offset: -2px; }
30
- .page-remote-block { box-shadow: inset 2px 0 0 var(--page-remote-color, currentColor); }
25
+ .blockeditor-remote-selection { background-color: color-mix(in srgb, var(--blockeditor-remote-color, currentColor) 22%, transparent); }
26
+ .blockeditor-remote-caret { position: relative; display: inline-block; width: 0; height: 1.1em; vertical-align: text-bottom; }
27
+ .blockeditor-remote-caret::before { content: ""; position: absolute; inset-inline-start: -1px; top: 0; bottom: 0; width: 2px; background: var(--blockeditor-remote-color, currentColor); }
28
+ .blockeditor-remote-name { position: absolute; inset-inline-start: -1px; bottom: 100%; padding: 1px 4px; border-radius: 4px; background: var(--blockeditor-remote-color, currentColor); color: #fff; font-size: 10px; line-height: 1.4; white-space: nowrap; pointer-events: none; }
29
+ .blockeditor-remote-cell { outline: 2px solid var(--blockeditor-remote-color, currentColor); outline-offset: -2px; }
30
+ .blockeditor-remote-block { box-shadow: inset 2px 0 0 var(--blockeditor-remote-color, currentColor); }
31
31
  `;
32
32
 
33
33
  // A fixed palette, chosen for contrast against both page themes. A peer's id
@@ -42,11 +42,11 @@ export const colorOf = (id) => {
42
42
 
43
43
  const caretElement = (doc, name, color) => {
44
44
  const bar = doc.createElement("span");
45
- bar.className = "page-remote-caret";
45
+ bar.className = "blockeditor-remote-caret";
46
46
  bar.setAttribute("aria-hidden", "true");
47
- bar.style.setProperty("--page-remote-color", color);
47
+ bar.style.setProperty("--blockeditor-remote-color", color);
48
48
  const tag = doc.createElement("span");
49
- tag.className = "page-remote-name";
49
+ tag.className = "blockeditor-remote-name";
50
50
  tag.textContent = name;
51
51
  bar.appendChild(tag);
52
52
  return bar;
@@ -78,19 +78,19 @@ export function decorationsFor({ binding, doc, snapshot, epoch }) {
78
78
  for (const session of person.sessions ?? []) {
79
79
  if (session.you) continue;
80
80
  const target = session.editing;
81
- if (!target || target.kind !== "page" || target.v !== PRESENCE_VERSION) continue;
81
+ if (!target || target.kind !== "blockeditor" || target.v !== PRESENCE_VERSION) continue;
82
82
  if (target.epoch !== epoch || target.generation !== generation) continue;
83
83
  const spot = decodeSelection(context, target.selection);
84
84
  if (!spot) continue;
85
85
  if (spot.kind === "text") {
86
- if (spot.to > spot.from) decorations.push(Decoration.inline(spot.from, spot.to, { class: "page-remote-selection", style: `--page-remote-color:${color}` }));
86
+ if (spot.to > spot.from) decorations.push(Decoration.inline(spot.from, spot.to, { class: "blockeditor-remote-selection", style: `--blockeditor-remote-color:${color}` }));
87
87
  decorations.push(Decoration.widget(spot.to, (view) => caretElement(view.dom.ownerDocument, name, color), { side: 1, key: `caret:${person.id}:${session.id}` }));
88
88
  } else if (spot.kind === "cell") {
89
- decorations.push(Decoration.node(spot.from, spot.to, { class: "page-remote-cell", style: `--page-remote-color:${color}` }));
89
+ decorations.push(Decoration.node(spot.from, spot.to, { class: "blockeditor-remote-cell", style: `--blockeditor-remote-color:${color}` }));
90
90
  decorations.push(Decoration.widget(spot.from + 1, (view) => caretElement(view.dom.ownerDocument, name, color), { side: -1, key: `cell:${person.id}:${session.id}` }));
91
91
  } else if (spot.kind === "blocks") {
92
92
  const [first] = spot.ranges;
93
- for (const range of spot.ranges) decorations.push(Decoration.node(range.from, range.to, { class: "page-remote-block", style: `--page-remote-color:${color}` }));
93
+ for (const range of spot.ranges) decorations.push(Decoration.node(range.from, range.to, { class: "blockeditor-remote-block", style: `--blockeditor-remote-color:${color}` }));
94
94
  decorations.push(Decoration.widget(first.from + 1, (view) => caretElement(view.dom.ownerDocument, name, color), { side: -1, key: `blocks:${person.id}:${session.id}` }));
95
95
  }
96
96
  }
@@ -133,7 +133,7 @@ export function createPageAwareness({ binding, view, store = presenceStore, epoc
133
133
  store.setTarget(null);
134
134
  return;
135
135
  }
136
- store.setTarget({ kind: "page", v: PRESENCE_VERSION, epoch, generation: binding.awareness.generation(), selection });
136
+ store.setTarget({ kind: "blockeditor", v: PRESENCE_VERSION, epoch, generation: binding.awareness.generation(), selection });
137
137
  };
138
138
 
139
139
  const rebuild = (next) => {
@@ -252,9 +252,9 @@ export async function installPageAwareness({ binding, view, store = presenceStor
252
252
  store.setCapabilities({
253
253
  supported: extensions.includes("presence"),
254
254
  recordAwareness: extensions.includes("record-awareness-v1"),
255
- pageAwareness: extensions.includes("page-awareness-v1"),
255
+ pageAwareness: extensions.includes("blockeditor-awareness-v1"),
256
256
  });
257
- if (!extensions.includes("page-awareness-v1")) return null;
257
+ if (!extensions.includes("blockeditor-awareness-v1")) return null;
258
258
  const epoch = answer.document?.awareness?.scope;
259
259
  if (typeof epoch !== "string" || !epoch) return null;
260
260
  return createPageAwareness({ binding, view, store, epoch });