@panphora/clayjs 1.15.2 → 1.17.0

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 (551) hide show
  1. package/README.md +4 -5
  2. package/THIRD-PARTY-NOTICES.md +16 -0
  3. package/compat/highlight-11.9.0.min.js +1213 -0
  4. package/compat/highlight-LICENSE +29 -0
  5. package/compat/journal-assets.lock.json +22 -0
  6. package/compat/overtype-2.6.1.min.js +116 -0
  7. package/compat/overtype-LICENSE +21 -0
  8. package/dist/all.js +90 -0
  9. package/dist/clay-dom.js +84 -0
  10. package/dist/clay-events.js +103 -0
  11. package/dist/clay-internals.js +85 -0
  12. package/dist/clay-options.js +93 -0
  13. package/dist/clay-router.js +98 -0
  14. package/dist/clay-ui.js +95 -0
  15. package/dist/clay-utils.js +84 -0
  16. package/dist/clay.js +146 -0
  17. package/dist/clay.standalone.js +84291 -57611
  18. package/dist/m/attrs/add-from-T5LJXFFI.js +1 -0
  19. package/dist/m/attrs/onaftersave-OKSWEJQF.js +1 -0
  20. package/dist/m/attrs/refetch-on-save-ZUE7PSCI.js +1 -0
  21. package/dist/m/attrs/save-freeze-MYNVXREN.js +1 -0
  22. package/dist/m/c-2AZDZWCK.js +1 -0
  23. package/dist/m/c-2SPVQSHC.js +1 -0
  24. package/dist/m/c-2YYDMLNZ.js +1 -0
  25. package/dist/m/c-336SGU5W.js +1 -0
  26. package/dist/m/c-33JJ6CBX.js +2 -0
  27. package/dist/m/c-3GJ4SDE2.js +1 -0
  28. package/dist/m/c-3H6BZDNJ.js +6 -0
  29. package/dist/m/c-3OWDMNJR.js +2 -0
  30. package/dist/m/c-3RUU4TPX.js +91 -0
  31. package/dist/m/c-3XIRD4NU.js +1 -0
  32. package/dist/m/c-46XYM55L.js +1 -0
  33. package/dist/m/c-4DFEJ2PO.js +3 -0
  34. package/dist/m/c-4JZDZFF6.js +1 -0
  35. package/dist/m/c-4L3BTFRK.js +1 -0
  36. package/dist/m/c-4Q2XTQ2N.js +2 -0
  37. package/dist/m/c-4WKFKEKF.js +1 -0
  38. package/dist/m/c-56QRZTZH.js +1 -0
  39. package/dist/m/c-5J4E7O4Y.js +4 -0
  40. package/dist/m/c-5LKDWQTL.js +19 -0
  41. package/dist/m/c-5QWQ7A5L.js +1 -0
  42. package/dist/m/c-5RJFTLSF.js +1 -0
  43. package/dist/m/c-5V3MOVJT.js +1 -0
  44. package/dist/m/c-5VYOWDTI.js +1 -0
  45. package/dist/m/c-5YIHJ3TJ.js +1 -0
  46. package/dist/m/c-6AZOGAPE.js +1 -0
  47. package/dist/m/c-6CRUUXOX.js +5 -0
  48. package/dist/m/c-6HMB24EV.js +1 -0
  49. package/dist/m/c-6KEVEQ7L.js +1 -0
  50. package/dist/m/c-6NC5I2TV.js +1 -0
  51. package/dist/m/c-74MGYWZF.js +1 -0
  52. package/dist/m/c-7DJDKPGL.js +1 -0
  53. package/dist/m/c-7OLLJPWP.js +1 -0
  54. package/dist/m/c-7SWMYKAI.js +1 -0
  55. package/dist/m/c-A3E3NIMC.js +1 -0
  56. package/dist/m/c-A7LS5UZW.js +1 -0
  57. package/dist/m/c-AMSNPOCX.js +1 -0
  58. package/dist/m/c-AQLFEATB.js +1 -0
  59. package/dist/m/c-B3JM5ZXC.js +1 -0
  60. package/dist/m/c-BBVGTQG7.js +1 -0
  61. package/dist/m/c-BPF2NSPV.js +1 -0
  62. package/dist/m/c-BQ6LOBEE.js +1 -0
  63. package/dist/m/c-BTDI4Y23.js +1 -0
  64. package/dist/m/c-BWFVSBDF.js +1 -0
  65. package/dist/m/c-BYTKL6UX.js +1 -0
  66. package/dist/m/c-CFHW5AMY.js +6 -0
  67. package/dist/m/c-CJ3A7ZKE.js +1 -0
  68. package/dist/m/c-CQHPGKX2.js +1 -0
  69. package/dist/m/c-CYWLPPUS.js +1 -0
  70. package/dist/m/c-CZUSOB7V.js +1 -0
  71. package/dist/m/c-DCFSEJZG.js +1 -0
  72. package/dist/m/c-DCXOXBF3.js +1 -0
  73. package/dist/m/c-DFS4XZJV.js +2 -0
  74. package/dist/m/c-DNENWY4O.js +1 -0
  75. package/dist/m/c-DNZ5IFM6.js +1 -0
  76. package/dist/m/c-E4LIR5X5.js +1 -0
  77. package/dist/m/c-EJTGYSLN.js +1 -0
  78. package/dist/m/c-EVCQ3BKA.js +1 -0
  79. package/dist/m/c-EZU56NQV.js +34 -0
  80. package/dist/m/c-FDNHHZY5.js +1 -0
  81. package/dist/m/c-FILLTJA7.js +1 -0
  82. package/dist/m/c-FRLHSY7E.js +1 -0
  83. package/dist/m/c-FROJUZMV.js +1 -0
  84. package/dist/m/c-G2JPKCTP.js +1 -0
  85. package/dist/m/c-GB6TU7WV.js +1 -0
  86. package/dist/m/c-GFJJEBTU.js +1 -0
  87. package/dist/m/c-GJAEGGXY.js +1 -0
  88. package/dist/m/c-GOWBDM6J.js +1 -0
  89. package/dist/m/c-GQ6F6IDK.js +1 -0
  90. package/dist/m/c-GSFIGKOP.js +1 -0
  91. package/dist/m/c-GY422N3Q.js +1 -0
  92. package/dist/m/c-H4LG442Q.js +1 -0
  93. package/dist/m/c-HA6INT6J.js +1 -0
  94. package/dist/m/c-HAWFNBYG.js +1 -0
  95. package/dist/m/c-HDDEEL77.js +1 -0
  96. package/dist/m/c-HFKUVACH.js +1 -0
  97. package/dist/m/c-HHDFCNHV.js +1 -0
  98. package/dist/m/c-HPURYVZS.js +2 -0
  99. package/dist/m/c-I2ZATQCD.js +2 -0
  100. package/dist/m/c-IEM4NTCS.js +1 -0
  101. package/dist/m/c-IJUK2GPW.js +1 -0
  102. package/dist/m/c-IW6GGOU2.js +1 -0
  103. package/dist/m/c-JAX2667S.js +6 -0
  104. package/dist/m/c-JDCX26UP.js +25 -0
  105. package/dist/m/c-JRKY44DK.js +1 -0
  106. package/dist/m/c-JU3NK25H.js +1 -0
  107. package/dist/m/c-KAU6E43D.js +1 -0
  108. package/dist/m/c-KBS3UV6B.js +1 -0
  109. package/dist/m/c-KGVGROZT.js +1 -0
  110. package/dist/m/c-KI7M4AY3.js +1 -0
  111. package/dist/m/c-KIPQJX4B.js +1 -0
  112. package/dist/m/c-KQ6TYJAP.js +135 -0
  113. package/dist/m/c-KU3ZFADW.js +1 -0
  114. package/dist/m/c-L4WQSC27.js +1 -0
  115. package/dist/m/c-LFPHCXY3.js +1 -0
  116. package/dist/m/c-LRPUO2MX.js +1 -0
  117. package/dist/m/c-LWLBHLVC.js +3 -0
  118. package/dist/m/c-LZJPNR2W.js +1 -0
  119. package/dist/m/c-M2L47S2X.js +7 -0
  120. package/dist/m/c-MFP2ZWD4.js +1 -0
  121. package/dist/m/c-MKSQ4Y57.js +1 -0
  122. package/dist/m/c-MTQHWQC3.js +1 -0
  123. package/dist/m/c-N326R2JD.js +1 -0
  124. package/dist/m/c-N7JPUPGC.js +1 -0
  125. package/dist/m/c-NRPJEVJS.js +1 -0
  126. package/dist/m/c-NUFFJVVJ.js +1 -0
  127. package/dist/m/c-NUHP2XDB.js +1 -0
  128. package/dist/m/c-NUHSPT5P.js +1 -0
  129. package/dist/m/c-O72HVYSA.js +1 -0
  130. package/dist/m/c-OCSZ4QGN.js +1 -0
  131. package/dist/m/c-OIBUTDXI.js +1 -0
  132. package/dist/m/c-OIDRZKAF.js +1 -0
  133. package/dist/m/c-OW5QIGG3.js +1 -0
  134. package/dist/m/c-P7KNDG34.js +1 -0
  135. package/dist/m/c-PGRCK73F.js +1 -0
  136. package/dist/m/c-PQ5LK6QE.js +993 -0
  137. package/dist/m/c-PS5ARVWR.js +9 -0
  138. package/dist/m/c-PZ4XEPUP.js +2 -0
  139. package/dist/m/c-Q622NS2Y.js +1 -0
  140. package/dist/m/c-Q7NQD65V.js +1 -0
  141. package/dist/m/c-QH63LQOS.js +1 -0
  142. package/dist/m/c-QHIWXHRO.js +1 -0
  143. package/dist/m/c-QHWJ5ZIJ.js +1 -0
  144. package/dist/m/c-QICP2YR5.js +1 -0
  145. package/dist/m/c-QJ4OM3G6.js +1 -0
  146. package/dist/m/c-QNKREOL6.js +1 -0
  147. package/dist/m/c-R3L7ZJGB.js +2 -0
  148. package/dist/m/c-R7IWVINQ.js +1 -0
  149. package/dist/m/c-RM7J7BV2.js +1 -0
  150. package/dist/m/c-RSMOODDU.js +1 -0
  151. package/dist/m/c-RVMWGXEJ.js +1 -0
  152. package/dist/m/c-RYUXUUBX.js +1 -0
  153. package/dist/m/c-S4ZEH46B.js +9 -0
  154. package/dist/m/c-S7H2DRCB.js +1 -0
  155. package/dist/m/c-S7LK4KUE.js +1 -0
  156. package/dist/m/c-SDAXDM2D.js +1 -0
  157. package/dist/m/c-SGIA4IEQ.js +188 -0
  158. package/dist/m/c-SMBZG5X4.js +1 -0
  159. package/dist/m/c-SMGU7RT3.js +235 -0
  160. package/dist/m/c-SPIORRYS.js +1 -0
  161. package/dist/m/c-SWLRT66R.js +1 -0
  162. package/dist/m/c-SZGYW5HT.js +1 -0
  163. package/dist/m/c-TCCA5BZN.js +2 -0
  164. package/dist/m/c-TCJTVAFN.js +1 -0
  165. package/dist/m/c-TJ6WOJL3.js +67 -0
  166. package/dist/m/c-TQEMVJCS.js +1 -0
  167. package/dist/m/c-TRS6TM6O.js +1 -0
  168. package/dist/m/c-TYLXX4NF.js +21 -0
  169. package/dist/m/c-U62OB4BT.js +1 -0
  170. package/dist/m/c-U6YWUAR3.js +8 -0
  171. package/dist/m/c-UDCSU5G5.js +2 -0
  172. package/dist/m/c-UDUUJPFZ.js +1 -0
  173. package/dist/m/c-UGHH4MB3.js +1 -0
  174. package/dist/m/c-UL2YUY52.js +1 -0
  175. package/dist/m/c-VAK7SG37.js +1 -0
  176. package/dist/m/c-VDSS4K3Z.js +1 -0
  177. package/dist/m/c-VE6ZMYMC.js +1 -0
  178. package/dist/m/c-VG5XIN4T.js +1 -0
  179. package/dist/m/c-VHM5XQHK.js +1 -0
  180. package/dist/m/c-VHNSBD2M.js +1 -0
  181. package/dist/m/c-VI5RMSVY.js +1 -0
  182. package/dist/m/c-VKEDWYDF.js +1 -0
  183. package/dist/m/c-VRZN56XY.js +1 -0
  184. package/dist/m/c-W5MCHEGA.js +1 -0
  185. package/dist/m/c-WDC5GVKU.js +1 -0
  186. package/dist/m/c-WN5AY3OZ.js +1 -0
  187. package/dist/m/c-WNYBLIOZ.js +1 -0
  188. package/dist/m/c-WRBKEJD4.js +1 -0
  189. package/dist/m/c-WWXIDH4O.js +28 -0
  190. package/dist/m/c-WYFDLITL.js +1 -0
  191. package/dist/m/c-XCNTK76U.js +1 -0
  192. package/dist/m/c-XMS6DPBR.js +1 -0
  193. package/dist/m/c-XN6MBKI6.js +67 -0
  194. package/dist/m/c-XPELB6E5.js +1 -0
  195. package/dist/m/c-XQXZAO7N.js +1 -0
  196. package/dist/m/c-Y5HBBIQ4.js +1 -0
  197. package/dist/m/c-ZE46QDIH.js +1 -0
  198. package/dist/m/c-ZUCOKOHT.js +1 -0
  199. package/dist/m/c-ZVPVXTJF.js +4 -0
  200. package/dist/m/c-ZVRKXKM7.js +1 -0
  201. package/dist/m/c-ZWIOJGMT.js +1 -0
  202. package/dist/m/core/admin-attrs-QHOKPJ7W.js +1 -0
  203. package/dist/m/core/autosave-HRYGUWV6.js +1 -0
  204. package/dist/m/core/conflict-notice-6MROJN2L.js +1 -0
  205. package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
  206. package/dist/m/core/host-permission-FNXVEFNE.js +1 -0
  207. package/dist/m/core/is-edit-mode-KTWA3O5N.js +1 -0
  208. package/dist/m/core/persist-JFYRHMRJ.js +3 -0
  209. package/dist/m/core/recovery-FLTCC2OC.js +1 -0
  210. package/dist/m/core/save-HQZ4S667.js +1 -0
  211. package/dist/m/core/save-core-KGQWFUNH.js +1 -0
  212. package/dist/m/core/snapshot-EGQZNT45.js +1 -0
  213. package/dist/m/core/unsaved-warning-IDJLD66H.js +1 -0
  214. package/dist/m/dom/all-APS5ZSKP.js +1 -0
  215. package/dist/m/dom/dom-helpers-L7OSR6S4.js +1 -0
  216. package/dist/m/events/index-RN2JNGBV.js +1 -0
  217. package/dist/m/internals/index-PVXGPUDA.js +1 -0
  218. package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
  219. package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
  220. package/dist/m/loader-S4I7GKXR.js +1 -0
  221. package/dist/m/manifest-4C419D67.js +1 -0
  222. package/dist/m/manifest-edit-B077F288.js +1 -0
  223. package/dist/m/manifest.json +3988 -0
  224. package/dist/m/options/options-EJMS3K24.js +1 -0
  225. package/dist/m/page/editor-NNJA4OKD.js +202 -0
  226. package/dist/m/plugins/ai-edit-QN4PMMNL.js +6 -0
  227. package/dist/m/plugins/bind-text-NX46N5UV.js +1 -0
  228. package/dist/m/plugins/cms-HP3LSTPN.js +2293 -0
  229. package/dist/m/plugins/demo-RM5IR6AO.js +1 -0
  230. package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
  231. package/dist/m/plugins/indicator-URDBBOQO.js +1 -0
  232. package/dist/m/plugins/live-HFWD4OIH.js +1 -0
  233. package/dist/m/plugins/menu-3HIEQSI6.js +138 -0
  234. package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
  235. package/dist/m/plugins/page-JOKB6XPR.js +2 -0
  236. package/dist/m/plugins/page-view-NXOJHNW6.js +1 -0
  237. package/dist/m/plugins/people-SGPJKANK.js +1 -0
  238. package/dist/m/plugins/quickcrop-QNY3PKLD.js +26 -0
  239. package/dist/m/plugins/quickcrop-QNY3PKLD.js.LEGAL.txt +7 -0
  240. package/dist/m/plugins/richclay-NRQDSWX6.js +517 -0
  241. package/dist/m/plugins/richclay-NRQDSWX6.js.LEGAL.txt +1 -0
  242. package/dist/m/plugins/sortable-GWT4PWDV.js +1 -0
  243. package/dist/m/plugins/source-4ZRSAIYM.js +1 -0
  244. package/dist/m/plugins/undo-VDA5P7FH.js +1 -0
  245. package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
  246. package/dist/m/plugins/upgrade-W7FG5VFG.js +1 -0
  247. package/dist/m/plugins/upload-WLYKK6TL.js +1 -0
  248. package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +21 -0
  249. package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +1 -0
  250. package/dist/m/plugins/wire-4N6GYAF6.js +1 -0
  251. package/dist/m/reader-core-DXYCURFF.js +68 -0
  252. package/dist/m/route/route-KOCZSEGN.js +1 -0
  253. package/dist/m/sync/live-sync-J7VMYTKS.js +1 -0
  254. package/dist/m/ui/index-3JLVI27O.js +1 -0
  255. package/dist/m/utils/index-4TJCVLT7.js +1 -0
  256. package/dist/m/vendor/Sortable.vendor-A755ITLN.js +3 -0
  257. package/dist/m/vendor/Sortable.vendor-A755ITLN.js.LEGAL.txt +1 -0
  258. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +6 -0
  259. package/dist/offline-manifest.json +4034 -0
  260. package/dist/page/themes/light-1.css +430 -0
  261. package/dist/server/core.cjs +23748 -22855
  262. package/dist/server/offline-runtime.cjs +4954 -0
  263. package/dist/server/offline-runtime.mjs +4923 -0
  264. package/dist/server/rooms.mjs +9 -0
  265. package/docs/guide.md +1078 -669
  266. package/entries/all.js +2 -0
  267. package/entries/clay-data.js +1 -1
  268. package/entries/clay-dom.js +2 -0
  269. package/entries/clay-events.js +2 -0
  270. package/entries/clay-internals.js +2 -0
  271. package/entries/clay-options.js +2 -0
  272. package/entries/clay-router.js +2 -0
  273. package/entries/clay-ui.js +2 -0
  274. package/entries/clay-utils.js +2 -0
  275. package/entries/clay.js +2 -0
  276. package/entries/sap.js +6 -6
  277. package/fonts/ibm-plex-mono-LICENSE +93 -0
  278. package/fonts/ibm-plex-mono-latin-300-normal.woff2 +0 -0
  279. package/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
  280. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  281. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  282. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  283. package/fonts/ibm-plex-sans-LICENSE +93 -0
  284. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  285. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  286. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  287. package/fonts/ibm-plex.css +71 -0
  288. package/fonts/inter-LICENSE +93 -0
  289. package/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  290. package/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  291. package/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  292. package/fonts/inter-greek-wght-normal.woff2 +0 -0
  293. package/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  294. package/fonts/inter-latin-wght-normal.woff2 +0 -0
  295. package/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  296. package/fonts/inter.css +62 -0
  297. package/fonts/newsreader-LICENSE +93 -0
  298. package/fonts/newsreader-latin-ext-opsz-normal.woff2 +0 -0
  299. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  300. package/fonts/newsreader-vietnamese-opsz-normal.woff2 +0 -0
  301. package/fonts/newsreader.css +26 -0
  302. package/fonts/work-sans-LICENSE +93 -0
  303. package/fonts/work-sans-latin-wght-normal.woff2 +0 -0
  304. package/fonts/work-sans.css +8 -0
  305. package/package.json +20 -3
  306. package/packed-contract.json +1 -0
  307. package/src/attrs/add-from.js +27 -4
  308. package/src/attrs/save-freeze.js +7 -52
  309. package/src/core/admin-attrs.js +119 -12
  310. package/src/core/admin-contenteditable.js +12 -10
  311. package/src/core/admin-inputs.js +14 -15
  312. package/src/core/admin-onclick.js +2 -0
  313. package/src/core/autosave.js +74 -21
  314. package/src/core/boot-phase.js +2 -1
  315. package/src/core/conflict-download.js +19 -23
  316. package/src/core/conflict-notice-ui.js +1173 -0
  317. package/src/core/conflict-notice.js +140 -697
  318. package/src/core/download.js +32 -0
  319. package/src/core/edit-mode.js +147 -14
  320. package/src/core/etag.js +30 -12
  321. package/src/core/host-meta.js +90 -8
  322. package/src/core/host-permission.js +29 -0
  323. package/src/core/is-edit-mode.js +145 -12
  324. package/src/core/mode-key.js +125 -0
  325. package/src/core/mode-lifecycle.js +544 -0
  326. package/src/core/mode-receipt.js +30 -0
  327. package/src/core/mode-refusal.js +13 -0
  328. package/src/core/mode-resources.js +52 -0
  329. package/src/core/mode-support.js +82 -0
  330. package/src/core/mode-switch.js +259 -0
  331. package/src/core/mode-transition.js +112 -0
  332. package/src/core/page-data-write.js +305 -0
  333. package/src/core/page-data.js +26 -227
  334. package/src/core/page-format-wire.js +4 -0
  335. package/src/core/page-format.js +58 -0
  336. package/src/core/persist.js +37 -9
  337. package/src/core/preserve-authored.js +114 -0
  338. package/src/core/recovery.js +772 -0
  339. package/src/core/runtime-url.js +105 -0
  340. package/src/core/save-core.js +113 -12
  341. package/src/core/save-replace.js +72 -0
  342. package/src/core/save-size-notice.js +134 -0
  343. package/src/core/save-size.js +140 -0
  344. package/src/core/save.js +806 -119
  345. package/src/core/snapshot.js +159 -11
  346. package/src/core/source-map.js +157 -9
  347. package/src/core/unsaved-warning.js +14 -4
  348. package/src/core/upgrade-run.js +408 -0
  349. package/src/core/version.js +18 -0
  350. package/src/lib/assets.js +30 -0
  351. package/src/lib/autosave-debug-gate.js +50 -0
  352. package/src/lib/autosave-scheduler.js +140 -0
  353. package/src/lib/boot-imports.js +8 -0
  354. package/src/lib/derived.js +3 -1
  355. package/src/lib/dirty-gate.js +15 -0
  356. package/src/lib/doc-storage.js +33 -0
  357. package/src/lib/effect.js +9 -4
  358. package/src/lib/mutation.js +4 -2
  359. package/src/lib/offline-runtime.js +226 -0
  360. package/src/lib/preload.js +48 -0
  361. package/src/lib/root-attrs.js +1 -1
  362. package/src/lib/trash.js +18 -0
  363. package/src/lib/undo/capture-move.js +152 -0
  364. package/src/live/local-channel.js +61 -0
  365. package/src/live/local-lane.js +60 -9
  366. package/src/live/server-lane.js +6 -3
  367. package/src/loader-logic.js +76 -15
  368. package/src/loader.js +218 -30
  369. package/src/page/adopt-boot.js +76 -22
  370. package/src/page/adopt.js +25 -4
  371. package/src/page/awareness.js +230 -0
  372. package/src/page/blocks.js +113 -6
  373. package/src/page/blocksel.js +20 -4
  374. package/src/page/board-face.js +13 -0
  375. package/src/page/board-panel.js +5 -17
  376. package/src/page/board-style.js +2 -13
  377. package/src/page/board-view.js +1 -11
  378. package/src/page/board-views.js +3 -0
  379. package/src/page/board.js +2 -2
  380. package/src/page/boot-pack.js +26 -0
  381. package/src/page/boot.js +237 -0
  382. package/src/page/chart-tables.js +11 -0
  383. package/src/page/chart.js +6 -10
  384. package/src/page/checklist.js +1 -1
  385. package/src/page/checkpoint-proof.js +289 -0
  386. package/src/page/chrome.js +431 -63
  387. package/src/page/csv-import.js +17 -2
  388. package/src/page/document-title-dom.js +52 -0
  389. package/src/page/document-title-runtime.js +90 -0
  390. package/src/page/document-title.js +12 -0
  391. package/src/page/drag.js +48 -13
  392. package/src/page/drop.js +2 -1
  393. package/src/page/editor.js +399 -90
  394. package/src/page/emoji-data.js +162 -0
  395. package/src/page/feature-loader.js +72 -0
  396. package/src/page/features/board.js +21 -0
  397. package/src/page/features/chart.js +11 -0
  398. package/src/page/features/code.js +13 -0
  399. package/src/page/features/dates.js +180 -0
  400. package/src/page/features/emoji.js +187 -0
  401. package/src/page/features/media.js +290 -0
  402. package/src/page/features/mentions.js +188 -0
  403. package/src/page/features/registry.js +28 -0
  404. package/src/page/features/table.js +383 -0
  405. package/src/page/{tool-view.js → features/tool.js} +33 -2
  406. package/src/page/format-codec.js +215 -0
  407. package/src/page/format-command.js +9 -0
  408. package/src/page/format-gate.js +179 -0
  409. package/src/page/format-host.js +54 -0
  410. package/src/page/format-level.js +157 -0
  411. package/src/page/format-native-commands.js +142 -0
  412. package/src/page/format-native.js +42 -0
  413. package/src/page/highlight-slot.js +23 -0
  414. package/src/page/highlight.js +30 -16
  415. package/src/page/history-intent.js +78 -0
  416. package/src/page/inline.js +45 -28
  417. package/src/page/items.js +67 -14
  418. package/src/page/link.js +1 -0
  419. package/src/page/markdown.js +112 -3
  420. package/src/page/media-views.js +3 -0
  421. package/src/page/media.js +33 -245
  422. package/src/page/mention-picker.js +99 -0
  423. package/src/page/merge.js +1 -1
  424. package/src/page/outline-cache.js +11 -0
  425. package/src/page/placeholder.js +27 -7
  426. package/src/page/projection.js +240 -51
  427. package/src/page/reader-patch.js +306 -0
  428. package/src/page/recover.js +1 -1
  429. package/src/page/rich/page-tool.js +1 -4
  430. package/src/page/runtime-owner.js +27 -0
  431. package/src/page/save-projection.js +46 -4
  432. package/src/page/save-states.js +1 -1
  433. package/src/page/schema.js +47 -0
  434. package/src/page/table-rect-key.js +3 -0
  435. package/src/page/table-select.js +4 -3
  436. package/src/page/table-style.js +1 -1
  437. package/src/page/table-views.js +3 -0
  438. package/src/page/table.js +8 -293
  439. package/src/page/themes/content.css +25 -1
  440. package/src/page/themes/light-1.css +163 -2
  441. package/src/page/themes/light-1.source.css +269 -0
  442. package/src/page/tool-capability.js +3 -0
  443. package/src/page/tool-views.js +3 -0
  444. package/src/page/view-hooks.js +32 -0
  445. package/src/page/view.js +390 -107
  446. package/src/page/ybind/awareness.js +172 -0
  447. package/src/page/ybind/binding.js +40 -5
  448. package/src/page/ybind/bridge.js +50 -47
  449. package/src/page/ybind/decode.js +52 -19
  450. package/src/page/ybind/doc.js +27 -2
  451. package/src/page/ybind/group.js +12 -3
  452. package/src/page/ybind/index.js +2 -0
  453. package/src/page/ybind/lineage.js +71 -0
  454. package/src/page/ybind/marks.js +26 -1
  455. package/src/page/ybind/migrate.js +48 -13
  456. package/src/page/ybind/moves.js +32 -21
  457. package/src/page/ybind/positions.js +50 -0
  458. package/src/page/ybind/provider.js +97 -24
  459. package/src/page/ybind/recovery-proof.js +191 -0
  460. package/src/page/ybind/recovery-replay.js +488 -0
  461. package/src/page/ybind/tokens.js +15 -6
  462. package/src/page/ybind/walk.js +10 -3
  463. package/src/page/ybind/writer.js +48 -4
  464. package/src/plugins/ai-edit.js +167 -32
  465. package/src/plugins/bind-text.js +218 -0
  466. package/src/plugins/cms.js +93 -0
  467. package/src/plugins/indicator.js +57 -5
  468. package/src/plugins/live.js +319 -35
  469. package/src/plugins/menu/claygoo.svg.js +1 -0
  470. package/src/plugins/menu/seo-head.js +323 -0
  471. package/src/plugins/menu/seo.js +849 -0
  472. package/src/plugins/menu.js +425 -0
  473. package/src/plugins/page-view.js +131 -0
  474. package/src/plugins/page.js +1131 -270
  475. package/src/plugins/people.js +158 -31
  476. package/src/plugins/richclay.js +119 -9
  477. package/src/plugins/sortable.js +370 -60
  478. package/src/plugins/source.js +189 -366
  479. package/src/plugins/undo.js +289 -19
  480. package/src/plugins/unsent.js +132 -0
  481. package/src/plugins/upgrade-run.js +1 -0
  482. package/src/plugins/upgrade.js +210 -0
  483. package/src/plugins/upload-task.js +571 -0
  484. package/src/plugins/upload-ui.js +265 -0
  485. package/src/plugins/upload.js +34 -153
  486. package/src/plugins/visitor-drafts/inert.js +32 -0
  487. package/src/plugins/visitor-drafts/intro.js +2 -1
  488. package/src/plugins/visitor-drafts/owner-chip.js +3 -0
  489. package/src/plugins/visitor-drafts/prompt.js +2 -2
  490. package/src/plugins/visitor-drafts-owner.js +85 -0
  491. package/src/plugins/visitor-drafts.js +31 -42
  492. package/src/plugins/wire.js +82 -1
  493. package/src/reader-core.js +26 -0
  494. package/src/recovery/conflicts.js +287 -0
  495. package/src/recovery/identity.js +154 -0
  496. package/src/recovery/store.js +792 -0
  497. package/src/recovery/undo.js +257 -0
  498. package/src/server/checkpoint/index.js +68 -4
  499. package/src/server/offline-runtime/index.js +214 -0
  500. package/src/server/rooms/index.js +2 -1
  501. package/src/server/rooms/live-data-rooms.js +2 -2
  502. package/src/server/rooms/page-admission.js +33 -0
  503. package/src/server/rooms/page-lineage.js +56 -0
  504. package/src/server/rooms/page-rebase.js +11 -5
  505. package/src/server/rooms/page-state.js +2 -0
  506. package/src/server/rooms/page-write-admission.js +9 -3
  507. package/src/source/session.js +705 -0
  508. package/src/sync/conflict-revert.js +5 -4
  509. package/src/sync/conflicts-edit.js +119 -0
  510. package/src/sync/conflicts-facade.js +49 -0
  511. package/src/sync/conflicts.js +99 -1
  512. package/src/sync/editing-marker.js +37 -6
  513. package/src/sync/editing-sender.js +8 -1
  514. package/src/sync/live-sync-edit.js +63 -0
  515. package/src/sync/live-sync.js +364 -119
  516. package/src/sync/local-presence.js +187 -0
  517. package/src/sync/merge-declarations.js +10 -0
  518. package/src/sync/merge-engine.js +12 -0
  519. package/src/sync/presence-store.js +412 -0
  520. package/src/sync/presence-target.js +243 -0
  521. package/src/sync/presence.js +72 -41
  522. package/src/sync/saved-handoff.js +200 -0
  523. package/src/sync/saved-stream.js +544 -0
  524. package/src/sync/version-guard.js +92 -0
  525. package/src/sync/wire-profile.js +81 -0
  526. package/src/ui/bevel-controls.js +2 -8
  527. package/src/ui/page-scheme.js +7 -0
  528. package/src/ui/skins/cms.js +1 -1
  529. package/src/ui/theme-config.js +150 -0
  530. package/src/ui/theme.js +6 -138
  531. package/src/ui/toast-lazy.js +11 -0
  532. package/src/vendor/LICENSES-optional.md +253 -0
  533. package/src/vendor/highlight.vendor.js +3 -0
  534. package/src/vendor/hljs-bash.vendor.js +2 -0
  535. package/src/vendor/hljs-css.vendor.js +2 -0
  536. package/src/vendor/hljs-javascript.vendor.js +2 -0
  537. package/src/vendor/hljs-json.vendor.js +2 -0
  538. package/src/vendor/hljs-python.vendor.js +2 -0
  539. package/src/vendor/hljs-xml.vendor.js +2 -0
  540. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  541. package/src/vendor/hyper-html-api-upgrade.vendor.js +5 -0
  542. package/src/vendor/hyper-morph-identity.vendor.js +2 -0
  543. package/src/vendor/hyper-morph-json.vendor.js +3 -0
  544. package/src/vendor/hyper-morph.vendor.js +6 -6
  545. package/src/vendor/hyper-undo.vendor.js +1 -1
  546. package/src/vendor/hypercms.vendor.js +169 -84
  547. package/src/vendor/marked.vendor.js +68 -0
  548. package/src/vendor/overtype.vendor.js +994 -0
  549. package/src/vendor/richclay.vendor.js +17 -17
  550. package/src/vendor/sap-core.vendor.js +5 -5
  551. package/src/vendor/sap-ownership.vendor.js +11 -7
@@ -1,6 +1,6 @@
1
1
  import { attrKeys, sameJson } from "./layout.js";
2
2
  import { blockJson } from "../model.js";
3
- import { marksKey, marksAttrs } from "./marks.js";
3
+ import { marksKey, marksFields } from "./marks.js";
4
4
  import { createProjectionMap } from "./projection-map.js";
5
5
 
6
6
  export const implicitFlags = (doc, visible) => {
@@ -19,7 +19,16 @@ export const implicitFlags = (doc, visible) => {
19
19
  });
20
20
  return out;
21
21
  };
22
- const pickAttrs = (node) => { const o = {}; for (const k of attrKeys(node.type)) o[k] = node.attrs[k]; if (node.type.name === "table") o.rows = blockJson(node).rows; return o; };
22
+ const opaqueOf = (node) => node.attrs.raw ?? null;
23
+ const pickAttrs = (node) => {
24
+ const raw = opaqueOf(node);
25
+ if (raw) { const o = { ...raw }; delete o.t; delete o.pos; return o; }
26
+ const o = {};
27
+ for (const k of attrKeys(node.type)) o[k] = node.attrs[k];
28
+ if (node.type.name === "table") o.rows = blockJson(node).rows;
29
+ return o;
30
+ };
31
+ const typeOfNode = (node) => opaqueOf(node)?.t ?? node.type.name;
23
32
 
24
33
  export function tokensOf(doc, alias = new Map(), visible = null) {
25
34
  const toks = [];
@@ -27,9 +36,9 @@ export function tokensOf(doc, alias = new Map(), visible = null) {
27
36
  doc.forEach((block, _o, index) => {
28
37
  const implicit = flags[index] && !alias.has(block.attrs.id);
29
38
  const brk = visible ? visible[index]?.brk ?? null : null;
30
- if (!implicit) toks.push({ k: "b", id: flags[index] && alias.has(block.attrs.id) ? alias.get(block.attrs.id) : block.attrs.id, t: block.type.name, attrs: pickAttrs(block), brk });
39
+ if (!implicit) toks.push({ k: "b", id: flags[index] && alias.has(block.attrs.id) ? alias.get(block.attrs.id) : block.attrs.id, t: typeOfNode(block), attrs: pickAttrs(block), brk });
31
40
  if (block.isTextblock) block.content.forEach((n) => {
32
- if (n.isText) { const ms = block.type.name === "code_block" ? [] : n.marks; const mk = marksKey(ms), ma = marksAttrs(ms); for (let i = 0; i < n.text.length; i++) toks.push({ k: "c", ch: n.text[i], mk, ma }); }
41
+ if (n.isText) { const ms = typeOfNode(block) === "code_block" ? [] : n.marks; const mk = marksKey(ms), ma = marksFields(ms); for (let i = 0; i < n.text.length; i++) toks.push({ k: "c", ch: n.text[i], mk, ma }); }
33
42
  else if (n.type.name === "hard_break") toks.push({ k: "h" });
34
43
  });
35
44
  });
@@ -74,8 +83,8 @@ export function textTokenRange(doc, from, to) {
74
83
  const a = Math.max(0, startOffset - cursor), b = Math.min(node.nodeSize, endOffset - cursor);
75
84
  if (a < b) {
76
85
  if (node.isText) {
77
- const marks = block.type.name === "code_block" ? [] : node.marks;
78
- const mk = marksKey(marks), ma = marksAttrs(marks);
86
+ const marks = typeOfNode(block) === "code_block" ? [] : node.marks;
87
+ const mk = marksKey(marks), ma = marksFields(marks);
79
88
  for (let i = a; i < b; i++) tokens.push({ k: "c", ch: node.text[i], mk, ma });
80
89
  } else if (node.type.name === "hard_break") tokens.push({ k: "h" });
81
90
  }
@@ -144,14 +144,21 @@ function walkFlow(pd, index) {
144
144
  const root = recoveredRoot(sourceKey, removalsBySource.get(sourceKey) ?? [], intents);
145
145
  const id = `recovered:${sourceKey}`;
146
146
  const t = root ? root.type : "paragraph";
147
- cur = { kind: "b", id, baseId: id, brk: { client: it.id.client, clock: it.id.clock }, implicit: false, runs: [], len: 1, atom: !schema.nodes[t]?.isTextblock, recovered: true, restored: !moved, sourceKey, t, fields: root ? root.attrs : null };
147
+ const opaque = !Object.hasOwn(schema.nodes, t);
148
+ cur = { kind: "b", id, baseId: id, brk: { client: it.id.client, clock: it.id.clock }, implicit: false, runs: [], len: 1, atom: !opaque && !schema.nodes[t]?.isTextblock, opaque, recovered: true, restored: !moved, sourceKey, t, fields: root ? root.attrs : null };
148
149
  segs.push(cur);
149
150
  prevId = id;
150
151
  };
151
152
  const open = (id, brk, implicit) => {
152
153
  cur = { kind: "b", id, baseId: id, brk, implicit, runs: [], len: implicit ? 0 : 1, atom: false, items: !implicit && (markerCount.get(id) ?? 0) > 1 ? [] : null };
153
- if (!implicit) { cur.t = typeOf(id); cur.atom = !schema.nodes[cur.t]?.isTextblock; }
154
- else cur.t = "paragraph";
154
+ if (!implicit) {
155
+ cur.t = typeOf(id);
156
+ // A type this runtime has no schema for still owns the text and marks up to
157
+ // the next block marker: it is not an atom whose following text becomes an
158
+ // implicit paragraph, and it is never rewritten as one.
159
+ cur.opaque = !Object.hasOwn(schema.nodes, cur.t);
160
+ cur.atom = !cur.opaque && !schema.nodes[cur.t]?.isTextblock;
161
+ } else cur.t = "paragraph";
155
162
  segs.push(cur);
156
163
  prevId = id;
157
164
  };
@@ -8,10 +8,23 @@ import { resolveTableFields, tableSeed, writeTable } from "./table.js";
8
8
  import { resolveBoardFields, writeBoard } from "./board.js";
9
9
  import { createOriginIndex } from "./origins.js";
10
10
 
11
+ const FIELD = (value) => (value === true ? {} : value);
12
+ const sameValue = (a, b) => sameJson(FIELD(a), FIELD(b));
13
+
11
14
  export function setAttrs(pd, id, t, attrs) {
12
15
  let m = pd.attrs.get(id);
13
16
  if (!m) { m = new Y.Map(); pd.attrs.set(id, m); }
14
17
  const type = schema.nodes[t];
18
+ // A block type this runtime has no schema for keeps its original record: no
19
+ // known field list to normalize it against, and never a paragraph in its place.
20
+ if (!type) {
21
+ if (m.get("t") !== t) m.set("t", t);
22
+ for (const [k, v] of Object.entries(attrs ?? {})) {
23
+ if (k === "t" || k === "pos") continue;
24
+ if (!sameJson(m.has(k) ? m.get(k) : undefined, v)) m.set(k, v);
25
+ }
26
+ return;
27
+ }
15
28
  const table = t === "table" && m.get("t") === "table" && !recoveredSource(id);
16
29
  const board = t === "board" && !recoveredSource(id);
17
30
  const before = table ? resolveTableFields(pd.tableFields, id, tableSeed(pd, id)) : board ? resolveBoardFields(pd.boardFields, id) : null;
@@ -46,6 +59,31 @@ export function liveKeys(ctx) {
46
59
  return out;
47
60
  }
48
61
 
62
+ // The live keys this runtime has no schema mark for, including the reserved
63
+ // compact-code bridge. A command never cancels them: they stay byte for byte
64
+ // until something that understands them rewrites them.
65
+ export function opaqueKeys(ctx) {
66
+ const cancelled = new Set();
67
+ for (const [k, v] of ctx) if (isCancel(k) && v) cancelled.add(k.split("!")[0]);
68
+ const out = [];
69
+ for (const [k, v] of ctx) if (!isCancel(k) && v !== null && v !== undefined && !cancelled.has(k) && !schema.marks[markName(k)]) out.push([k, v]);
70
+ return out;
71
+ }
72
+
73
+ // The attributes an inserted unit carries over from its neighbours: every known
74
+ // key as before, and an opaque key only when the editor's mark set still names
75
+ // it, so text at a span boundary does not inherit a format nobody can read.
76
+ export function baseAttributes(ctx, want = {}) {
77
+ const out = {};
78
+ let opaque = false;
79
+ for (const [k, v] of ctx) if (isCancel(k) || schema.marks[markName(k)]) out[k] = v;
80
+ for (const [k, v] of opaqueKeys(ctx)) {
81
+ opaque = true;
82
+ if (want[markName(k)] !== undefined) out[k] = v;
83
+ }
84
+ return { attrs: out, opaque };
85
+ }
86
+
49
87
  export function runAttributes(pd, ctx, want) {
50
88
  const rid = mintOp(pd);
51
89
  const changes = {};
@@ -54,12 +92,17 @@ export function runAttributes(pd, ctx, want) {
54
92
  for (const [k, v] of have) {
55
93
  const n = markName(k);
56
94
  const w = want[n];
57
- if (w !== undefined && sameJson(w === true ? true : w, v)) kept.add(n);
95
+ if (w !== undefined && sameValue(w, v)) kept.add(n);
58
96
  else changes[`${k}!${rid}`] = true;
59
97
  }
98
+ for (const [k, v] of opaqueKeys(ctx)) {
99
+ const n = markName(k);
100
+ if (want[n] !== undefined && sameValue(want[n], v)) kept.add(n);
101
+ }
60
102
  for (const [n, w] of Object.entries(want)) if (!kept.has(n)) changes[`${n}~${mintOp(pd)}`] = w;
61
- if (!Object.keys(changes).length) return undefined;
62
- return { ...Object.fromEntries(ctx), ...changes };
103
+ const base = baseAttributes(ctx, want);
104
+ if (!Object.keys(changes).length && !base.opaque) return undefined;
105
+ return { ...base.attrs, ...changes };
63
106
  }
64
107
 
65
108
  export const CHUNK_CHARS = 2000;
@@ -119,7 +162,7 @@ function insertChunk(pd, yIdx, toks, fences = null) {
119
162
  i += run.length;
120
163
  run = "";
121
164
  };
122
- const embed = (e) => { const attrs = Object.fromEntries(ctxAt(pd, i)); beforeFence(pd, i, fences, () => pd.flow.insertEmbed(i, e, attrs)); i += 1; };
165
+ const embed = (e) => { const attrs = baseAttributes(ctxAt(pd, i)).attrs; beforeFence(pd, i, fences, () => pd.flow.insertEmbed(i, e, attrs)); i += 1; };
123
166
  for (const t of toks) {
124
167
  if (t.k === "c") { if (runMk !== null && t.mk !== runMk) flush(); runMk = t.mk; runMa = t.ma; run += t.ch; }
125
168
  else if (t.k === "h") { flush(); runMk = null; embed({ br: 1 }); }
@@ -249,6 +292,7 @@ export function applyRegion(ctx, ta, oldToks, newToks, oldSurvives = null) {
249
292
  else insertTokens(pd, map.toPhysical(e.at, "before"), e.i.toks, ctx.tx, ctx.fences);
250
293
  }
251
294
  for (const [ob, nb] of attrs) ctx.tx(() => {
295
+ if (!schema.nodes[nb.t]) return;
252
296
  const source = ctx.recovered.get(ob.id);
253
297
  if (source) {
254
298
  if (ob.t !== nb.t) pd.gone.set(attrKey(source, "t"), nb.t);
@@ -41,10 +41,9 @@
41
41
  * invisible to the mutation system (its status text changes constantly), absent from
42
42
  * every snapshot and from what peers receive.
43
43
  */
44
- import { HyperMorph } from "../vendor/hyper-morph.vendor.js";
45
- import { mergeTagRecognizers } from "../sync/merge-tags.js";
44
+ import { loadedEngine, mergeEngine } from "../sync/merge-engine.js";
46
45
  import Mutation from "../lib/mutation.js";
47
- import { isEditMode } from "../core/is-edit-mode.js";
46
+ import { startsAtImport } from "../core/is-edit-mode.js";
48
47
  import { holdAllSaves, releaseAllSaves } from "../core/save.js";
49
48
  import { STRIP_FROM_SAVE, SNAPSHOT_REMOVE_SELECTOR } from "../lib/region-policy.js";
50
49
  import { enableContentEditable } from "../core/admin-contenteditable.js";
@@ -81,7 +80,10 @@ let barMode = null; // "working", "ready", "saving" or "error"
81
80
  let barLifecycle = null;
82
81
  let barCloseTimer = null;
83
82
  let panelPointerDown = false; // the last pointerdown began in the panel: its click is not a page click
84
- const drafts = new WeakMap(); // compose text a target keeps while the panel is shut
83
+ let drafts = new WeakMap(); // compose text a target keeps while the panel is shut
84
+ // A WeakMap cannot be read back, and an exit has to know whether anything typed here is
85
+ // still unsent, so the targets that hold a draft are counted beside it.
86
+ const drafted = new Set();
85
87
  let anchorEl = null; // element the panel is currently anchored to
86
88
  let chipTarget = null; // unit the hover chip currently points at
87
89
  let chipHideTimer = null;
@@ -92,6 +94,24 @@ let selectionTimer = null;
92
94
  let anchorRange = null; // the selection the open panel was raised for
93
95
  let returnFocus = null; // where focus goes back to when the panel closes
94
96
  let helperState = null; // "ready" or "unavailable"
97
+ let listeners = null; // the AbortController every handler one session added listens on
98
+ let observers = []; // the page observers that session opened
99
+ let styleEl = null; // the one stylesheet the chrome needed
100
+ let chromeToken = 0; // the latest init(); a teardown retires the one before it
101
+
102
+ function dropDraft(el) {
103
+ drafts.delete(el);
104
+ drafted.delete(el);
105
+ }
106
+
107
+ function keepDraft(el, text) {
108
+ if (!text) {
109
+ dropDraft(el);
110
+ return;
111
+ }
112
+ drafts.set(el, text);
113
+ drafted.add(el);
114
+ }
95
115
 
96
116
  const HIGHLIGHT = 'clay-ai-edit';
97
117
  const OFF_MESSAGE = 'AI editing is turned off. Turn it on from the app’s menu.';
@@ -190,6 +210,7 @@ function strippedBodyHTML() {
190
210
  // ---------------------------------------------------------------- morphing
191
211
 
192
212
  function morphTo(el, content, scripts) {
213
+ const { HyperMorph } = loadedEngine();
193
214
  return HyperMorph.morph(el, content, { morphStyle: 'outerHTML', restoreFocus: true, scripts });
194
215
  }
195
216
 
@@ -201,6 +222,7 @@ function morphTo(el, content, scripts) {
201
222
  // against the snapshot the model saw, so data the user changed mid-flight survives
202
223
  // the reply. Rewinds must restore the snapshot exactly, so they disable merging.
203
224
  function sessionMorph(s, content, { rewind = false } = {}) {
225
+ const { HyperMorph, mergeTagRecognizers } = loadedEngine();
204
226
  // handle:false: a reply's scripts never run, so the preview cannot execute code
205
227
  // before Keep, and neither can a rewind.
206
228
  const scripts = rewind
@@ -300,7 +322,7 @@ async function sendRequest(el, comment, quote) {
300
322
  }
301
323
  // The sent comment is this target's draft until the reply is ready: Error, Stop, a
302
324
  // refused reply, a morph failure and a host cancellation all leave it in place.
303
- drafts.set(el, comment);
325
+ keepDraft(el, comment);
304
326
  closePanelForSend();
305
327
  session.paused = true;
306
328
  pauseObservers();
@@ -321,6 +343,9 @@ async function sendRequest(el, comment, quote) {
321
343
  session.handle = handle;
322
344
  const outcome = await handle.done;
323
345
  if (!session || session.handle !== handle) return;
346
+ // The engine is fetched here, where a reply is applied, not at boot.
347
+ await mergeEngine();
348
+ if (!session || session.handle !== handle) return;
324
349
  if (outcome.state === 'done') onDone(outcome.result || {});
325
350
  else if (outcome.state === 'cancelled') onError('HTML Clay stopped this edit.');
326
351
  else onError(outcome.error || 'the helper reported an error');
@@ -417,7 +442,7 @@ function onDone(payload) {
417
442
  return;
418
443
  }
419
444
  positionChrome();
420
- drafts.delete(session.el); // delivered: the comment is no longer this target's draft
445
+ dropDraft(session.el); // delivered: the comment is no longer this target's draft
421
446
  showBar('ready');
422
447
  setBarStatus(readyText(warnings, payload.model), warnings.length ? 'warn' : '');
423
448
  }
@@ -444,7 +469,7 @@ function revertSession() {
444
469
  session = null;
445
470
  if (!s) return;
446
471
  try {
447
- if (s.state === 'requesting' || s.state === 'deciding') {
472
+ if ((s.state === 'requesting' || s.state === 'deciding') && loadedEngine()) {
448
473
  sessionMorph(s, s.snapshot, { rewind: true });
449
474
  }
450
475
  } finally {
@@ -507,15 +532,16 @@ function marked(el, name, value) {
507
532
  }
508
533
 
509
534
  function buildChrome() {
510
- const style = document.createElement('style');
511
- style.setAttribute('clay', RUNTIME_ONLY);
512
- style.textContent = `
535
+ const { signal } = listeners;
536
+ styleEl = document.createElement('style');
537
+ styleEl.setAttribute('clay', RUNTIME_ONLY);
538
+ styleEl.textContent = `
513
539
  [editmode\\:contenteditable][contenteditable]:focus {
514
540
  outline: 1px solid #4a4a6a; outline-offset: 4px; border-radius: 2px;
515
541
  }
516
542
  ::highlight(clay-ai-edit) { background-color: ${TOKENS['brass-soft']} !important; }
517
543
  `;
518
- document.head.appendChild(style);
544
+ document.head.appendChild(styleEl);
519
545
 
520
546
  const scheme = `color-scheme:${pageScheme()}`;
521
547
 
@@ -641,24 +667,28 @@ function buildChrome() {
641
667
  openPanel(document.body, quoteFromSelection(document.body));
642
668
  });
643
669
 
644
- window.addEventListener('scroll', () => { positionChrome(); hideChip(); }, { passive: true });
645
- window.addEventListener('resize', () => { placeBubble(); if (panel && !panel.hidden) fitTextarea(); positionChrome(); }, { passive: true });
670
+ window.addEventListener('scroll', () => { positionChrome(); hideChip(); }, { passive: true, signal });
671
+ window.addEventListener('resize', () => { placeBubble(); if (panel && !panel.hidden) fitTextarea(); positionChrome(); }, { passive: true, signal });
646
672
  // The CMS toggle can arrive after this chrome, and a docked sidebar slides it left.
647
673
  const replace = () => { placeBubble(); positionChrome(); };
648
- document.addEventListener('hcms:open', () => { replace(); setTimeout(replace, 350); });
649
- document.addEventListener('hcms:close', () => { replace(); setTimeout(replace, 350); });
650
- new MutationObserver((records) => {
674
+ document.addEventListener('hcms:open', () => { replace(); setTimeout(replace, 350); }, { signal });
675
+ document.addEventListener('hcms:close', () => { replace(); setTimeout(replace, 350); }, { signal });
676
+ const cmsToggle = new MutationObserver((records) => {
651
677
  syncBubble();
652
678
  const touched = (nodes) => [...nodes].some((n) => n.nodeType === 1 && n.hasAttribute('data-hcms-toggle-host'));
653
679
  if (records.some((r) => touched(r.addedNodes) || touched(r.removedNodes))) replace();
654
- }).observe(document.body, { childList: true });
680
+ });
681
+ cmsToggle.observe(document.body, { childList: true });
682
+ observers.push(cmsToggle);
655
683
 
656
684
  // A target removed from the page ends its edit, so the save hold and the paused
657
685
  // observers never outlive it. A running bar is no exception: it is torn down with
658
686
  // the target it was describing.
659
- new MutationObserver(() => {
687
+ const targetGone = new MutationObserver(() => {
660
688
  if (anchorEl && !anchorEl.isConnected) abandonTarget();
661
- }).observe(document.body, { childList: true, subtree: true });
689
+ });
690
+ targetGone.observe(document.body, { childList: true, subtree: true });
691
+ observers.push(targetGone);
662
692
  }
663
693
 
664
694
  // The panel's height changes as the box grows and the status line fills, so it is
@@ -709,6 +739,7 @@ function applyHelperState() {
709
739
  // The host's toggle can change while the page is open, so each opening asks again.
710
740
  function refreshHelperState() {
711
741
  helperStateNow().then((state) => {
742
+ if (!panel) return; // the session this asked for has ended
712
743
  helperState = state || 'unavailable';
713
744
  if (!panel.hidden && !session) applyHelperState();
714
745
  else syncBubble();
@@ -939,8 +970,7 @@ function openPanel(el, quote) {
939
970
  // panel is opened on that target again.
940
971
  function storeDraft() {
941
972
  if (!anchorEl) return;
942
- if (textarea.value) drafts.set(anchorEl, textarea.value);
943
- else drafts.delete(anchorEl);
973
+ keepDraft(anchorEl, textarea.value);
944
974
  }
945
975
 
946
976
  function closePanel() {
@@ -1023,6 +1053,7 @@ function escapeIsMine(event) {
1023
1053
  }
1024
1054
 
1025
1055
  function wireInteractions() {
1056
+ const { signal } = listeners;
1026
1057
  // Clicks: text units are contenteditable (caret) and figures keep their own
1027
1058
  // interactivity, so neither is intercepted, and nothing here prevents the click from
1028
1059
  // landing. Any click on the page away from ClayJS chrome closes the box, a click
@@ -1033,7 +1064,7 @@ function wireInteractions() {
1033
1064
  // box and ends on the page is still the box's, not a click away from it.
1034
1065
  document.addEventListener('pointerdown', (event) => {
1035
1066
  panelPointerDown = panel.contains(event.target);
1036
- }, { capture: true });
1067
+ }, { capture: true, signal });
1037
1068
 
1038
1069
  document.addEventListener('click', (event) => {
1039
1070
  const startedInPanel = panelPointerDown;
@@ -1050,7 +1081,7 @@ function wireInteractions() {
1050
1081
  } else if (!panel.hidden) {
1051
1082
  closePanel();
1052
1083
  }
1053
- });
1084
+ }, { signal });
1054
1085
 
1055
1086
  // Hover chip: follows the unit under the cursor while no panel is open.
1056
1087
  let lastMove = 0;
@@ -1064,7 +1095,7 @@ function wireInteractions() {
1064
1095
  const unit = unitFrom(event.target);
1065
1096
  if (unit) showChipFor(unit);
1066
1097
  else scheduleChipHide();
1067
- });
1098
+ }, { signal });
1068
1099
 
1069
1100
  document.addEventListener('keydown', (event) => {
1070
1101
  if (isShortcut(event)) {
@@ -1094,26 +1125,35 @@ function wireInteractions() {
1094
1125
  if (barMode === 'error') { closeBar(); return; }
1095
1126
  if (barMode) return;
1096
1127
  if (!panel.hidden) closePanel();
1097
- });
1128
+ }, { signal });
1098
1129
 
1099
1130
  document.addEventListener('selectionchange', () => {
1100
1131
  clearTimeout(selectionTimer);
1101
1132
  selectionTimer = setTimeout(showChipForSelection, 150);
1102
- });
1133
+ }, { signal });
1103
1134
  }
1104
1135
 
1105
1136
  // ---------------------------------------------------------------- boot
1106
1137
 
1107
- async function init() {
1108
- if (!isEditMode || panel) return; // an owner/editor feature, built once
1138
+ async function loadHelper() {
1139
+ const token = ++chromeToken;
1109
1140
  helperState = await helperStateNow();
1110
- // No ai-edit helper on this host: stay dormant. A concurrent open() that got here
1111
- // first may also have built the chrome already.
1141
+ return token === chromeToken;
1142
+ }
1143
+
1144
+ function build() {
1112
1145
  if (!helperState || panel) return;
1146
+ listeners = new AbortController();
1113
1147
  buildChrome();
1114
1148
  wireInteractions();
1115
1149
  }
1116
1150
 
1151
+ async function init() {
1152
+ if (!startsAtImport() || panel) return; // an owner/editor feature, built once
1153
+ if (!(await loadHelper())) return;
1154
+ build();
1155
+ }
1156
+
1117
1157
  // A target the composer can be opened on: a connected content element of this
1118
1158
  // document. ClayJS chrome, transient UI, everything the save strips and everything
1119
1159
  // that holds no text are not targets.
@@ -1141,11 +1181,106 @@ async function open(el, { prompt } = {}) {
1141
1181
  textarea.focus();
1142
1182
  return true;
1143
1183
  }
1144
- if (typeof prompt === 'string' && !drafts.has(el)) drafts.set(el, prompt);
1184
+ if (typeof prompt === 'string' && !drafts.has(el)) keepDraft(el, prompt);
1145
1185
  openPanel(el, null);
1146
1186
  return true;
1147
1187
  }
1148
1188
 
1149
- onDomReady(() => { init().catch(() => {}); });
1189
+ /**
1190
+ * A committed exit: the chrome, every handler this session added and every page
1191
+ * observer go. What the person typed is a draft, and an exit that could not represent it
1192
+ * never gets here, so nothing unsent is thrown away by this.
1193
+ */
1194
+ function teardown() {
1195
+ chromeToken++;
1196
+ const s = session;
1197
+ session = null;
1198
+ try {
1199
+ s?.handle?.cancel();
1200
+ if (s) {
1201
+ if (s.paused) resumeObservers();
1202
+ if (s.held) releaseAllSaves();
1203
+ }
1204
+ } catch {}
1205
+ clearTimeout(selectionTimer);
1206
+ selectionTimer = null;
1207
+ clearTimeout(chipHideTimer);
1208
+ chipHideTimer = null;
1209
+ clearTimeout(barCloseTimer);
1210
+ barCloseTimer = null;
1211
+ clearHighlight();
1212
+ listeners?.abort();
1213
+ listeners = null;
1214
+ for (const observer of observers) observer.disconnect();
1215
+ observers = [];
1216
+ for (const node of [styleEl, ring, panel, bar, chip, docBubble]) node?.remove();
1217
+ styleEl = ring = panel = textarea = statusEl = pointer = bar = barStatus = null;
1218
+ barStop = barKeep = barRevert = barClose = chip = docBubble = null;
1219
+ buttons = {};
1220
+ barMode = null;
1221
+ barLifecycle = null;
1222
+ anchorEl = null;
1223
+ anchorRange = null;
1224
+ returnFocus = null;
1225
+ pendingSelection = undefined;
1226
+ chipTarget = null;
1227
+ chipSelection = undefined;
1228
+ panelPointerDown = false;
1229
+ helperState = null;
1230
+ drafts = new WeakMap();
1231
+ drafted.clear();
1232
+ }
1233
+
1234
+ /**
1235
+ * ai-edit as a mode lease.
1236
+ *
1237
+ * Preparing this lease asks the host whether it lists a ready helper, which is safe while
1238
+ * the committed mode is still view; the commit that enters edit builds the chrome, inert
1239
+ * until somebody opens it, exactly as `clay.aiEdit.init()` does. What a session adds is
1240
+ * everything still unresolved — a request on the wire, a preview waiting for Keep or
1241
+ * Revert, a comment typed and not sent — so `quiesce` refuses the exit and `resume`
1242
+ * leaves that same preview, comment and selection timer exactly as they were.
1243
+ * `dispose` (after the barrier) stops every editor-only handler and takes the chrome
1244
+ * down, so a save or selection event in view draws nothing. `initial: true` adopts the
1245
+ * chrome the boot's own import built, and asks the host nothing a second time.
1246
+ */
1247
+ export function aiEditParticipant() {
1248
+ return {
1249
+ id: "clay:ai-edit",
1250
+ order: 950, // after the wire (900): this request is sent through it
1251
+ prepare(context = {}) {
1252
+ const initial = context.initial === true;
1253
+ return {
1254
+ ready: initial ? boot : loadHelper(),
1255
+ enable() {
1256
+ if (!initial) build();
1257
+ },
1258
+ quiesce() {
1259
+ const unsent = (panel && !panel.hidden && textarea.value.trim() !== '') ||
1260
+ [...drafted].some((el) => el.isConnected);
1261
+ const pending = [];
1262
+ if (session) pending.push(session.id);
1263
+ if (barMode === 'saving') pending.push('save');
1264
+ if (unsent) pending.push('prompt');
1265
+ if (!pending.length) return;
1266
+ throw Object.assign(new Error('clay.aiEdit: an edit here is still unresolved'), {
1267
+ code: 'pending-input',
1268
+ pending,
1269
+ });
1270
+ },
1271
+ resume() {},
1272
+ dispose() {
1273
+ teardown();
1274
+ },
1275
+ };
1276
+ },
1277
+ };
1278
+ }
1279
+
1280
+ // The boot's own answer, which an explicit edit boot's exit adopts rather than re-asking.
1281
+ let boot = null;
1282
+ onDomReady(() => { boot = init().then(() => {}, () => {}); });
1283
+
1284
+ export { aiEditParticipant as modeParticipant };
1150
1285
 
1151
1286
  export const aiEdit = { init, open };