@panphora/clayjs 1.15.2 → 1.16.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 (506) 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 +83090 -58706
  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-NWPPXOCZ.js +1 -0
  21. package/dist/m/attrs/save-freeze-FUP4Q4X3.js +1 -0
  22. package/dist/m/c-2AZDZWCK.js +1 -0
  23. package/dist/m/c-2JL6E75N.js +1 -0
  24. package/dist/m/c-2M73252O.js +1 -0
  25. package/dist/m/c-2SPVQSHC.js +1 -0
  26. package/dist/m/c-33HUYEDI.js +1 -0
  27. package/dist/m/c-33JJ6CBX.js +2 -0
  28. package/dist/m/c-35NBGMZB.js +1 -0
  29. package/dist/m/c-3GJ4SDE2.js +1 -0
  30. package/dist/m/c-3IQRECYS.js +1 -0
  31. package/dist/m/c-3OWDMNJR.js +2 -0
  32. package/dist/m/c-46XYM55L.js +1 -0
  33. package/dist/m/c-4B6H4G5V.js +1 -0
  34. package/dist/m/c-4DRMS4QG.js +9 -0
  35. package/dist/m/c-4FFHZM32.js +1 -0
  36. package/dist/m/c-4JZDZFF6.js +1 -0
  37. package/dist/m/c-4L3BTFRK.js +1 -0
  38. package/dist/m/c-4Q2XTQ2N.js +2 -0
  39. package/dist/m/c-4WKFKEKF.js +1 -0
  40. package/dist/m/c-4YSCRICO.js +1 -0
  41. package/dist/m/c-54RCDCBU.js +1 -0
  42. package/dist/m/c-5M4YEKCX.js +4 -0
  43. package/dist/m/c-5R4PTRG2.js +1 -0
  44. package/dist/m/c-5V3MOVJT.js +1 -0
  45. package/dist/m/c-5VYOWDTI.js +1 -0
  46. package/dist/m/c-5Y6TNX5W.js +34 -0
  47. package/dist/m/c-5YIHJ3TJ.js +1 -0
  48. package/dist/m/c-66O32MV3.js +28 -0
  49. package/dist/m/c-6HMB24EV.js +1 -0
  50. package/dist/m/c-7DJDKPGL.js +1 -0
  51. package/dist/m/c-7O6AFR65.js +1 -0
  52. package/dist/m/c-7SWMYKAI.js +1 -0
  53. package/dist/m/c-7VGZ7RU5.js +1 -0
  54. package/dist/m/c-A7LS5UZW.js +1 -0
  55. package/dist/m/c-ANQTJ3B5.js +2 -0
  56. package/dist/m/c-AQLFEATB.js +1 -0
  57. package/dist/m/c-AYVOREBE.js +1 -0
  58. package/dist/m/c-B2ZXPOS3.js +3 -0
  59. package/dist/m/c-BBVGTQG7.js +1 -0
  60. package/dist/m/c-BMIZKMPV.js +1 -0
  61. package/dist/m/c-BTDI4Y23.js +1 -0
  62. package/dist/m/c-BWFVSBDF.js +1 -0
  63. package/dist/m/c-BYTKL6UX.js +1 -0
  64. package/dist/m/c-CFHW5AMY.js +6 -0
  65. package/dist/m/c-CJZSLMVQ.js +1 -0
  66. package/dist/m/c-CYWLPPUS.js +1 -0
  67. package/dist/m/c-CZUSOB7V.js +1 -0
  68. package/dist/m/c-DBE6ORSI.js +1 -0
  69. package/dist/m/c-DCFSEJZG.js +1 -0
  70. package/dist/m/c-DNZ5IFM6.js +1 -0
  71. package/dist/m/c-EDHUR4MX.js +1 -0
  72. package/dist/m/c-EJTGYSLN.js +1 -0
  73. package/dist/m/c-EY6UOM54.js +1 -0
  74. package/dist/m/c-F3CKJTJS.js +1 -0
  75. package/dist/m/c-F7Y6FLPY.js +1 -0
  76. package/dist/m/c-FA4DZVTC.js +1 -0
  77. package/dist/m/c-FILLTJA7.js +1 -0
  78. package/dist/m/c-G2BUZITR.js +1 -0
  79. package/dist/m/c-GB3JTV6E.js +1 -0
  80. package/dist/m/c-GBGWXTX4.js +1 -0
  81. package/dist/m/c-GHBCCVG4.js +1 -0
  82. package/dist/m/c-GJAEGGXY.js +1 -0
  83. package/dist/m/c-GOWBDM6J.js +1 -0
  84. package/dist/m/c-GQ6F6IDK.js +1 -0
  85. package/dist/m/c-H4LG442Q.js +1 -0
  86. package/dist/m/c-HA6INT6J.js +1 -0
  87. package/dist/m/c-HPURYVZS.js +2 -0
  88. package/dist/m/c-HRDCD3WN.js +67 -0
  89. package/dist/m/c-HT33EQRN.js +1 -0
  90. package/dist/m/c-HVYXIS72.js +1 -0
  91. package/dist/m/c-IB52R237.js +1 -0
  92. package/dist/m/c-IJUK2GPW.js +1 -0
  93. package/dist/m/c-IKWNE4R4.js +1 -0
  94. package/dist/m/c-IU3IYEU6.js +1 -0
  95. package/dist/m/c-IZ2EBRAJ.js +1 -0
  96. package/dist/m/c-J7CA54IP.js +1 -0
  97. package/dist/m/c-JAX2667S.js +6 -0
  98. package/dist/m/c-KI7M4AY3.js +1 -0
  99. package/dist/m/c-KIPQJX4B.js +1 -0
  100. package/dist/m/c-KKWLDOPM.js +1 -0
  101. package/dist/m/c-KR4UW4KW.js +1 -0
  102. package/dist/m/c-KY3VJGUA.js +8 -0
  103. package/dist/m/c-L4WQSC27.js +1 -0
  104. package/dist/m/c-LFPHCXY3.js +1 -0
  105. package/dist/m/c-LR4CGEPT.js +91 -0
  106. package/dist/m/c-LRPUO2MX.js +1 -0
  107. package/dist/m/c-LTGMXXKO.js +1 -0
  108. package/dist/m/c-LXENEBV7.js +1 -0
  109. package/dist/m/c-LZJPNR2W.js +1 -0
  110. package/dist/m/c-MMUIUUZG.js +2 -0
  111. package/dist/m/c-MTQHWQC3.js +1 -0
  112. package/dist/m/c-N326R2JD.js +1 -0
  113. package/dist/m/c-N7JPUPGC.js +1 -0
  114. package/dist/m/c-NMJRXKPC.js +8 -0
  115. package/dist/m/c-NRPJEVJS.js +1 -0
  116. package/dist/m/c-NUHP2XDB.js +1 -0
  117. package/dist/m/c-NZMNXVF6.js +1 -0
  118. package/dist/m/c-OCSZ4QGN.js +1 -0
  119. package/dist/m/c-OIBUTDXI.js +1 -0
  120. package/dist/m/c-OIDRZKAF.js +1 -0
  121. package/dist/m/c-OYTFP5QN.js +1 -0
  122. package/dist/m/c-P7KNDG34.js +1 -0
  123. package/dist/m/c-PPOSCAQD.js +1 -0
  124. package/dist/m/c-PQ5LK6QE.js +993 -0
  125. package/dist/m/c-PZ4XEPUP.js +2 -0
  126. package/dist/m/c-QHIWXHRO.js +1 -0
  127. package/dist/m/c-QHWJ5ZIJ.js +1 -0
  128. package/dist/m/c-QIX5BJYN.js +1 -0
  129. package/dist/m/c-QNKREOL6.js +1 -0
  130. package/dist/m/c-QQHEX6HA.js +1 -0
  131. package/dist/m/c-R7RMANJI.js +9 -0
  132. package/dist/m/c-RI4NT75Y.js +1 -0
  133. package/dist/m/c-RM7J7BV2.js +1 -0
  134. package/dist/m/c-RUJKSUQW.js +1 -0
  135. package/dist/m/c-RVMWGXEJ.js +1 -0
  136. package/dist/m/c-S7H2DRCB.js +1 -0
  137. package/dist/m/c-S7LK4KUE.js +1 -0
  138. package/dist/m/c-SGCCC4FW.js +25 -0
  139. package/dist/m/c-SGIA4IEQ.js +188 -0
  140. package/dist/m/c-SWRJFCNN.js +1 -0
  141. package/dist/m/c-T4VO4OCB.js +1 -0
  142. package/dist/m/c-TCCA5BZN.js +2 -0
  143. package/dist/m/c-TQEMVJCS.js +1 -0
  144. package/dist/m/c-TQZ2JYHD.js +1 -0
  145. package/dist/m/c-TR6RMDQ7.js +3 -0
  146. package/dist/m/c-TRS6TM6O.js +1 -0
  147. package/dist/m/c-UGHH4MB3.js +1 -0
  148. package/dist/m/c-UKHTLWUW.js +1 -0
  149. package/dist/m/c-UL2YUY52.js +1 -0
  150. package/dist/m/c-UNN7LWCT.js +1 -0
  151. package/dist/m/c-UXBMRDZB.js +235 -0
  152. package/dist/m/c-V7PZZRG7.js +1 -0
  153. package/dist/m/c-VAK7SG37.js +1 -0
  154. package/dist/m/c-VDSS4K3Z.js +1 -0
  155. package/dist/m/c-VFX45IZQ.js +1 -0
  156. package/dist/m/c-VHNSBD2M.js +1 -0
  157. package/dist/m/c-VKEDWYDF.js +1 -0
  158. package/dist/m/c-VL52ADDR.js +1 -0
  159. package/dist/m/c-WN5AY3OZ.js +1 -0
  160. package/dist/m/c-WNYBLIOZ.js +1 -0
  161. package/dist/m/c-WRBKEJD4.js +1 -0
  162. package/dist/m/c-WWDBYXLK.js +1 -0
  163. package/dist/m/c-WYFDLITL.js +1 -0
  164. package/dist/m/c-XN6MBKI6.js +67 -0
  165. package/dist/m/c-XNNW2CWK.js +1 -0
  166. package/dist/m/c-XPELB6E5.js +1 -0
  167. package/dist/m/c-XQXZAO7N.js +1 -0
  168. package/dist/m/c-Y6USGTS5.js +5 -0
  169. package/dist/m/c-YDH7Q3NY.js +1 -0
  170. package/dist/m/c-YG2NJYS6.js +135 -0
  171. package/dist/m/c-Z36J63PP.js +1 -0
  172. package/dist/m/c-Z4VRC7GU.js +7 -0
  173. package/dist/m/c-Z6K52OLP.js +19 -0
  174. package/dist/m/c-ZDY2MDAA.js +21 -0
  175. package/dist/m/c-ZE46QDIH.js +1 -0
  176. package/dist/m/c-ZUCOKOHT.js +1 -0
  177. package/dist/m/core/admin-attrs-VGNLAUDK.js +1 -0
  178. package/dist/m/core/autosave-E4PEFPZ6.js +1 -0
  179. package/dist/m/core/conflict-notice-VQ6Y64PH.js +1 -0
  180. package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
  181. package/dist/m/core/host-permission-XN3K2IW3.js +1 -0
  182. package/dist/m/core/is-edit-mode-JXHDVGUL.js +1 -0
  183. package/dist/m/core/persist-HKHA66IY.js +3 -0
  184. package/dist/m/core/recovery-TBBSICE2.js +1 -0
  185. package/dist/m/core/save-WUCHTQBE.js +1 -0
  186. package/dist/m/core/save-core-FJVHT5M3.js +1 -0
  187. package/dist/m/core/snapshot-F3SWDZMM.js +1 -0
  188. package/dist/m/core/unsaved-warning-STKBHE6G.js +1 -0
  189. package/dist/m/dom/all-APS5ZSKP.js +1 -0
  190. package/dist/m/dom/dom-helpers-L7OSR6S4.js +1 -0
  191. package/dist/m/events/index-23DPTUT4.js +1 -0
  192. package/dist/m/internals/index-WOVAP3RU.js +1 -0
  193. package/dist/m/lib/cache-bust-CGWT6ZJI.js +1 -0
  194. package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
  195. package/dist/m/loader-HXX6YNH5.js +1 -0
  196. package/dist/m/manifest-F22DEDAC.js +1 -0
  197. package/dist/m/manifest-edit-C3C7376B.js +1 -0
  198. package/dist/m/manifest.json +3491 -0
  199. package/dist/m/options/options-2LH76JQ3.js +1 -0
  200. package/dist/m/page/editor-XUTB2HB2.js +189 -0
  201. package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +6 -0
  202. package/dist/m/plugins/bind-text-NX46N5UV.js +1 -0
  203. package/dist/m/plugins/cms-HP3LSTPN.js +2293 -0
  204. package/dist/m/plugins/demo-ZIBFAA3R.js +1 -0
  205. package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
  206. package/dist/m/plugins/indicator-PV4YW2ID.js +1 -0
  207. package/dist/m/plugins/live-NUTTXSBS.js +1 -0
  208. package/dist/m/plugins/menu-HCKO3DSP.js +138 -0
  209. package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
  210. package/dist/m/plugins/page-KSUH7G6J.js +2 -0
  211. package/dist/m/plugins/page-view-W73OW7WI.js +1 -0
  212. package/dist/m/plugins/people-AJJ32YCG.js +1 -0
  213. package/dist/m/plugins/quickcrop-QNY3PKLD.js +26 -0
  214. package/dist/m/plugins/quickcrop-QNY3PKLD.js.LEGAL.txt +7 -0
  215. package/dist/m/plugins/richclay-RC3L2F6X.js +517 -0
  216. package/dist/m/plugins/richclay-RC3L2F6X.js.LEGAL.txt +1 -0
  217. package/dist/m/plugins/sortable-YRMQX3ZU.js +1 -0
  218. package/dist/m/plugins/source-KHK5UUSK.js +1 -0
  219. package/dist/m/plugins/undo-OF2L3CXU.js +1 -0
  220. package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
  221. package/dist/m/plugins/upgrade-ZNYODP5Y.js +1 -0
  222. package/dist/m/plugins/upload-XSGNILUY.js +1 -0
  223. package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +21 -0
  224. package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +1 -0
  225. package/dist/m/plugins/wire-ZWVWKF76.js +1 -0
  226. package/dist/m/reader-core-ILWLXVMP.js +68 -0
  227. package/dist/m/route/route-KOCZSEGN.js +1 -0
  228. package/dist/m/sync/live-sync-4AUVXVJU.js +1 -0
  229. package/dist/m/ui/index-EVHKMKBL.js +1 -0
  230. package/dist/m/utils/index-4TJCVLT7.js +1 -0
  231. package/dist/m/vendor/Sortable.vendor-A755ITLN.js +3 -0
  232. package/dist/m/vendor/Sortable.vendor-A755ITLN.js.LEGAL.txt +1 -0
  233. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +6 -0
  234. package/dist/offline-manifest.json +3800 -0
  235. package/dist/page/themes/light-1.css +406 -0
  236. package/dist/server/core.cjs +23662 -22860
  237. package/dist/server/offline-runtime.cjs +4954 -0
  238. package/dist/server/offline-runtime.mjs +4923 -0
  239. package/dist/server/rooms.mjs +9 -0
  240. package/docs/guide.md +1078 -669
  241. package/entries/all.js +2 -0
  242. package/entries/clay-data.js +1 -1
  243. package/entries/clay-dom.js +2 -0
  244. package/entries/clay-events.js +2 -0
  245. package/entries/clay-internals.js +2 -0
  246. package/entries/clay-options.js +2 -0
  247. package/entries/clay-router.js +2 -0
  248. package/entries/clay-ui.js +2 -0
  249. package/entries/clay-utils.js +2 -0
  250. package/entries/clay.js +2 -0
  251. package/entries/sap.js +6 -6
  252. package/fonts/ibm-plex-mono-LICENSE +93 -0
  253. package/fonts/ibm-plex-mono-latin-300-normal.woff2 +0 -0
  254. package/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
  255. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  256. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  257. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  258. package/fonts/ibm-plex-sans-LICENSE +93 -0
  259. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  260. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  261. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  262. package/fonts/ibm-plex.css +71 -0
  263. package/fonts/inter-LICENSE +93 -0
  264. package/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  265. package/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  266. package/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  267. package/fonts/inter-greek-wght-normal.woff2 +0 -0
  268. package/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  269. package/fonts/inter-latin-wght-normal.woff2 +0 -0
  270. package/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  271. package/fonts/inter.css +62 -0
  272. package/fonts/newsreader-LICENSE +93 -0
  273. package/fonts/newsreader-latin-ext-opsz-normal.woff2 +0 -0
  274. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  275. package/fonts/newsreader-vietnamese-opsz-normal.woff2 +0 -0
  276. package/fonts/newsreader.css +26 -0
  277. package/fonts/work-sans-LICENSE +93 -0
  278. package/fonts/work-sans-latin-wght-normal.woff2 +0 -0
  279. package/fonts/work-sans.css +8 -0
  280. package/package.json +20 -3
  281. package/packed-contract.json +1 -0
  282. package/src/attrs/add-from.js +27 -4
  283. package/src/attrs/save-freeze.js +7 -52
  284. package/src/core/admin-attrs.js +119 -12
  285. package/src/core/admin-contenteditable.js +12 -10
  286. package/src/core/admin-inputs.js +14 -15
  287. package/src/core/admin-onclick.js +2 -0
  288. package/src/core/autosave.js +74 -21
  289. package/src/core/conflict-download.js +22 -0
  290. package/src/core/conflict-notice-ui.js +1173 -0
  291. package/src/core/conflict-notice.js +140 -697
  292. package/src/core/edit-mode.js +147 -14
  293. package/src/core/etag.js +30 -12
  294. package/src/core/host-meta.js +72 -8
  295. package/src/core/host-permission.js +29 -0
  296. package/src/core/is-edit-mode.js +145 -12
  297. package/src/core/mode-key.js +125 -0
  298. package/src/core/mode-lifecycle.js +544 -0
  299. package/src/core/mode-receipt.js +30 -0
  300. package/src/core/mode-refusal.js +13 -0
  301. package/src/core/mode-resources.js +52 -0
  302. package/src/core/mode-support.js +82 -0
  303. package/src/core/mode-switch.js +259 -0
  304. package/src/core/mode-transition.js +112 -0
  305. package/src/core/page-data-write.js +305 -0
  306. package/src/core/page-data.js +26 -227
  307. package/src/core/page-format-wire.js +4 -0
  308. package/src/core/page-format.js +58 -0
  309. package/src/core/persist.js +37 -9
  310. package/src/core/preserve-authored.js +114 -0
  311. package/src/core/recovery.js +772 -0
  312. package/src/core/runtime-url.js +105 -0
  313. package/src/core/save-core.js +109 -12
  314. package/src/core/save-replace.js +72 -0
  315. package/src/core/save-size-notice.js +134 -0
  316. package/src/core/save-size.js +140 -0
  317. package/src/core/save.js +805 -118
  318. package/src/core/snapshot.js +121 -11
  319. package/src/core/source-map.js +157 -9
  320. package/src/core/unsaved-warning.js +14 -4
  321. package/src/core/upgrade-run.js +408 -0
  322. package/src/core/version.js +18 -0
  323. package/src/lib/assets.js +30 -0
  324. package/src/lib/autosave-debug-gate.js +50 -0
  325. package/src/lib/autosave-scheduler.js +141 -0
  326. package/src/lib/boot-imports.js +8 -0
  327. package/src/lib/derived.js +3 -1
  328. package/src/lib/dirty-gate.js +15 -0
  329. package/src/lib/doc-storage.js +33 -0
  330. package/src/lib/effect.js +9 -4
  331. package/src/lib/mutation.js +4 -2
  332. package/src/lib/offline-runtime.js +226 -0
  333. package/src/lib/preload.js +48 -0
  334. package/src/lib/root-attrs.js +1 -1
  335. package/src/lib/trash.js +18 -0
  336. package/src/lib/undo/capture-move.js +152 -0
  337. package/src/live/local-channel.js +61 -0
  338. package/src/live/local-lane.js +60 -9
  339. package/src/live/server-lane.js +6 -3
  340. package/src/loader-logic.js +76 -15
  341. package/src/loader.js +218 -30
  342. package/src/page/adopt-boot.js +76 -22
  343. package/src/page/adopt.js +25 -4
  344. package/src/page/awareness.js +230 -0
  345. package/src/page/blocks.js +79 -5
  346. package/src/page/blocksel.js +20 -4
  347. package/src/page/board-face.js +13 -0
  348. package/src/page/board-panel.js +5 -17
  349. package/src/page/board-style.js +2 -13
  350. package/src/page/board-view.js +1 -11
  351. package/src/page/board-views.js +17 -0
  352. package/src/page/board.js +2 -2
  353. package/src/page/boot-pack.js +26 -0
  354. package/src/page/boot.js +237 -0
  355. package/src/page/checklist.js +1 -1
  356. package/src/page/chrome.js +411 -61
  357. package/src/page/csv-import.js +17 -2
  358. package/src/page/document-title-dom.js +40 -0
  359. package/src/page/document-title-runtime.js +76 -0
  360. package/src/page/document-title.js +9 -0
  361. package/src/page/drag.js +48 -13
  362. package/src/page/drop.js +2 -1
  363. package/src/page/editor.js +330 -86
  364. package/src/page/feature-loader.js +72 -0
  365. package/src/page/features/board.js +12 -0
  366. package/src/page/features/code.js +13 -0
  367. package/src/page/features/media.js +290 -0
  368. package/src/page/features/registry.js +23 -0
  369. package/src/page/features/table.js +383 -0
  370. package/src/page/{tool-view.js → features/tool.js} +33 -2
  371. package/src/page/format-codec.js +215 -0
  372. package/src/page/format-gate.js +179 -0
  373. package/src/page/format-level.js +157 -0
  374. package/src/page/highlight-slot.js +23 -0
  375. package/src/page/highlight.js +30 -16
  376. package/src/page/history-intent.js +78 -0
  377. package/src/page/inline.js +18 -25
  378. package/src/page/items.js +52 -11
  379. package/src/page/link.js +1 -0
  380. package/src/page/markdown.js +86 -1
  381. package/src/page/media-views.js +3 -0
  382. package/src/page/media.js +33 -245
  383. package/src/page/outline-cache.js +11 -0
  384. package/src/page/placeholder.js +27 -7
  385. package/src/page/projection.js +196 -50
  386. package/src/page/reader-patch.js +306 -0
  387. package/src/page/recover.js +1 -1
  388. package/src/page/rich/page-tool.js +1 -4
  389. package/src/page/runtime-owner.js +27 -0
  390. package/src/page/save-projection.js +7 -4
  391. package/src/page/schema.js +14 -0
  392. package/src/page/table-rect-key.js +3 -0
  393. package/src/page/table-select.js +4 -3
  394. package/src/page/table-style.js +1 -1
  395. package/src/page/table-views.js +3 -0
  396. package/src/page/table.js +8 -293
  397. package/src/page/themes/light-1.css +139 -2
  398. package/src/page/themes/light-1.source.css +269 -0
  399. package/src/page/tool-capability.js +3 -0
  400. package/src/page/tool-views.js +3 -0
  401. package/src/page/view.js +390 -107
  402. package/src/page/ybind/awareness.js +172 -0
  403. package/src/page/ybind/binding.js +9 -1
  404. package/src/page/ybind/bridge.js +50 -47
  405. package/src/page/ybind/decode.js +52 -19
  406. package/src/page/ybind/doc.js +27 -2
  407. package/src/page/ybind/group.js +12 -3
  408. package/src/page/ybind/index.js +2 -0
  409. package/src/page/ybind/lineage.js +71 -0
  410. package/src/page/ybind/marks.js +25 -1
  411. package/src/page/ybind/migrate.js +48 -13
  412. package/src/page/ybind/moves.js +32 -21
  413. package/src/page/ybind/positions.js +50 -0
  414. package/src/page/ybind/provider.js +84 -22
  415. package/src/page/ybind/recovery-proof.js +191 -0
  416. package/src/page/ybind/recovery-replay.js +130 -0
  417. package/src/page/ybind/tokens.js +15 -6
  418. package/src/page/ybind/walk.js +10 -3
  419. package/src/page/ybind/writer.js +48 -4
  420. package/src/plugins/ai-edit.js +167 -32
  421. package/src/plugins/bind-text.js +218 -0
  422. package/src/plugins/cms.js +93 -0
  423. package/src/plugins/indicator.js +57 -5
  424. package/src/plugins/live.js +282 -34
  425. package/src/plugins/menu/claygoo.svg.js +1 -0
  426. package/src/plugins/menu/seo-head.js +323 -0
  427. package/src/plugins/menu/seo.js +849 -0
  428. package/src/plugins/menu.js +425 -0
  429. package/src/plugins/page-view.js +131 -0
  430. package/src/plugins/page.js +1022 -275
  431. package/src/plugins/people.js +158 -31
  432. package/src/plugins/richclay.js +119 -9
  433. package/src/plugins/sortable.js +370 -60
  434. package/src/plugins/source.js +189 -366
  435. package/src/plugins/undo.js +289 -19
  436. package/src/plugins/unsent.js +132 -0
  437. package/src/plugins/upgrade-run.js +1 -0
  438. package/src/plugins/upgrade.js +210 -0
  439. package/src/plugins/upload-task.js +571 -0
  440. package/src/plugins/upload-ui.js +265 -0
  441. package/src/plugins/upload.js +34 -153
  442. package/src/plugins/visitor-drafts/inert.js +32 -0
  443. package/src/plugins/visitor-drafts/owner-chip.js +3 -0
  444. package/src/plugins/visitor-drafts/prompt.js +2 -2
  445. package/src/plugins/visitor-drafts-owner.js +85 -0
  446. package/src/plugins/visitor-drafts.js +31 -42
  447. package/src/plugins/wire.js +82 -1
  448. package/src/reader-core.js +26 -0
  449. package/src/recovery/conflicts.js +287 -0
  450. package/src/recovery/identity.js +154 -0
  451. package/src/recovery/store.js +792 -0
  452. package/src/recovery/undo.js +257 -0
  453. package/src/server/checkpoint/index.js +68 -4
  454. package/src/server/offline-runtime/index.js +214 -0
  455. package/src/server/rooms/index.js +2 -1
  456. package/src/server/rooms/live-data-rooms.js +2 -2
  457. package/src/server/rooms/page-admission.js +33 -0
  458. package/src/server/rooms/page-lineage.js +56 -0
  459. package/src/server/rooms/page-rebase.js +11 -5
  460. package/src/server/rooms/page-state.js +2 -0
  461. package/src/server/rooms/page-write-admission.js +9 -3
  462. package/src/source/session.js +705 -0
  463. package/src/sync/conflict-revert.js +5 -4
  464. package/src/sync/conflicts-edit.js +119 -0
  465. package/src/sync/conflicts-facade.js +49 -0
  466. package/src/sync/conflicts.js +99 -1
  467. package/src/sync/editing-marker.js +37 -6
  468. package/src/sync/editing-sender.js +8 -1
  469. package/src/sync/live-sync-edit.js +63 -0
  470. package/src/sync/live-sync.js +364 -119
  471. package/src/sync/local-presence.js +187 -0
  472. package/src/sync/merge-declarations.js +10 -0
  473. package/src/sync/merge-engine.js +12 -0
  474. package/src/sync/presence-store.js +412 -0
  475. package/src/sync/presence-target.js +243 -0
  476. package/src/sync/presence.js +72 -41
  477. package/src/sync/saved-handoff.js +200 -0
  478. package/src/sync/saved-stream.js +535 -0
  479. package/src/sync/version-guard.js +92 -0
  480. package/src/sync/wire-profile.js +81 -0
  481. package/src/ui/bevel-controls.js +2 -8
  482. package/src/ui/page-scheme.js +7 -0
  483. package/src/ui/skins/cms.js +1 -1
  484. package/src/ui/theme-config.js +150 -0
  485. package/src/ui/theme.js +6 -138
  486. package/src/ui/toast-lazy.js +11 -0
  487. package/src/vendor/LICENSES-optional.md +253 -0
  488. package/src/vendor/highlight.vendor.js +3 -0
  489. package/src/vendor/hljs-bash.vendor.js +2 -0
  490. package/src/vendor/hljs-css.vendor.js +2 -0
  491. package/src/vendor/hljs-javascript.vendor.js +2 -0
  492. package/src/vendor/hljs-json.vendor.js +2 -0
  493. package/src/vendor/hljs-python.vendor.js +2 -0
  494. package/src/vendor/hljs-xml.vendor.js +2 -0
  495. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  496. package/src/vendor/hyper-html-api-upgrade.vendor.js +5 -0
  497. package/src/vendor/hyper-morph-identity.vendor.js +2 -0
  498. package/src/vendor/hyper-morph-json.vendor.js +3 -0
  499. package/src/vendor/hyper-morph.vendor.js +6 -6
  500. package/src/vendor/hyper-undo.vendor.js +1 -1
  501. package/src/vendor/hypercms.vendor.js +169 -84
  502. package/src/vendor/marked.vendor.js +68 -0
  503. package/src/vendor/overtype.vendor.js +994 -0
  504. package/src/vendor/richclay.vendor.js +17 -17
  505. package/src/vendor/sap-core.vendor.js +5 -5
  506. package/src/vendor/sap-ownership.vendor.js +11 -7
@@ -19,12 +19,9 @@
19
19
  */
20
20
 
21
21
  import { hostMeta, hostMetaOutcome } from "../core/host-meta.js";
22
- import { isEditMode } from "../core/is-edit-mode.js";
22
+ import { isEditMode, modeState } from "../core/is-edit-mode.js";
23
23
 
24
24
  const editing = () => (window.clay && "isEditMode" in window.clay ? !!window.clay.isEditMode : isEditMode);
25
- import { bevelDialog, dismissOf, keepFocusIn } from "../ui/bevel-dialog.js";
26
- import { bevelBox, bevelButton, bevelInput, bevelText } from "../ui/bevel-controls.js";
27
- import { resolveTheme } from "../ui/theme.js";
28
25
  import { colorIndexOf, initialsOf } from "../lib/person-face.js";
29
26
 
30
27
  const ID = /^[A-Za-z0-9_-]{8,64}$/;
@@ -41,6 +38,7 @@ let asking = null;
41
38
  let askingRoot = null;
42
39
  let cancelAsking = null;
43
40
  let lastRegistry = "";
41
+ let dialogLoad = null;
44
42
 
45
43
  function clean(person) {
46
44
  if (!person || typeof person !== "object") return null;
@@ -140,6 +138,7 @@ async function refresh(fresh) {
140
138
  }
141
139
 
142
140
  function get(id) {
141
+ observe();
143
142
  const key = String(id ?? "");
144
143
  const live = (host.me?.id === key && host.me) || host.members?.find((person) => person.id === key);
145
144
  if (live) return record(live);
@@ -149,6 +148,7 @@ function get(id) {
149
148
  }
150
149
 
151
150
  function list() {
151
+ observe();
152
152
  return [...saved().keys()].map(get);
153
153
  }
154
154
 
@@ -208,6 +208,16 @@ function rename(name) {
208
208
  return record(next);
209
209
  }
210
210
 
211
+ // Same session and still editing: the two facts an await can invalidate.
212
+ const stale = (generation) => generation !== modeState().generation || !editing();
213
+
214
+ function refuseIfStale(generation) {
215
+ if (!stale(generation)) return;
216
+ throw Object.assign(new Error("clay.author: the page left edit mode before your name was recorded"), {
217
+ code: "stale-document",
218
+ });
219
+ }
220
+
211
221
  function unavailable() {
212
222
  return Object.assign(new Error("Can't reach the host to check who you are. Try again in a moment."), { code: "people-unavailable" });
213
223
  }
@@ -220,11 +230,16 @@ function unavailable() {
220
230
  export async function author(el, attr = "data-by") {
221
231
  if (!window.clay?.isEditMode) throw new Error("clay.author: this page is not in edit mode");
222
232
  if (!(el instanceof Element)) throw new TypeError("clay.author: needs an element");
233
+ // Attribution belongs to the session that asked for it. Asking the host who I am and
234
+ // asking a person their name both take long enough for the page to leave edit, and a
235
+ // stamp recorded afterwards would credit a session that has already ended.
236
+ const generation = modeState().generation;
223
237
  // A host that names people can change who you are while the page is open (a sign-out,
224
238
  // a rename in the app), so a stamp asks it again first. If it cannot answer now, nothing
225
239
  // is stamped: the last answer might name someone who is no longer here.
226
240
  if (host.supports) {
227
241
  const meta = await hostMeta({ fresh: true });
242
+ refuseIfStale(generation);
228
243
  const fresh = hostMetaOutcome(meta);
229
244
  if (fresh === "none") throw Object.assign(new Error("This page lost its connection to the host. Reload it to keep editing."), { code: "people-unavailable" });
230
245
  if (fresh !== "ok") throw unavailable();
@@ -232,11 +247,13 @@ export async function author(el, attr = "data-by") {
232
247
  if (host.unavailable) throw Object.assign(new Error("The app hosting this file can't name you right now. Check its Profile settings, then try again."), { code: "people-unavailable" });
233
248
  } else if (outcome !== "ok" && outcome !== "none") {
234
249
  await refresh(outcome === "failed");
250
+ refuseIfStale(generation);
235
251
  }
236
252
  if (outcome === "failed") throw unavailable();
237
253
  let me = currentMe();
238
254
  if (!me) {
239
255
  const chosen = await askName();
256
+ refuseIfStale(generation);
240
257
  if (!chosen) return null;
241
258
  writeLocal(chosen);
242
259
  me = record(chosen);
@@ -247,12 +264,38 @@ export async function author(el, attr = "data-by") {
247
264
  return me;
248
265
  }
249
266
 
267
+ // The dialog and the theme it paints with are this page's first-use download: a
268
+ // reader is nobody and is asked nothing, so it never fetches them.
269
+ function loadDialogUI() {
270
+ dialogLoad ??= Promise.all([
271
+ import("../ui/bevel-dialog.js"),
272
+ import("../ui/bevel-controls.js"),
273
+ import("../ui/theme.js"),
274
+ ]).then(([dialog, controls, theme]) => ({ ...dialog, ...controls, resolveTheme: theme.resolveTheme }))
275
+ .catch((error) => {
276
+ dialogLoad = null;
277
+ throw error;
278
+ });
279
+ return dialogLoad;
280
+ }
281
+
250
282
  function askName() {
251
- if (asking && askingRoot?.isConnected) return asking;
283
+ if (asking && (askingRoot ? askingRoot.isConnected : true)) return asking;
252
284
  if (asking) cancelAsking();
253
- asking = new Promise((resolve) => {
254
- const theme = resolveTheme();
255
- const { root, overlay, panel, heading, body, footer, close } = bevelDialog({
285
+ asking = promptForName().catch((error) => {
286
+ asking = null;
287
+ askingRoot = null;
288
+ cancelAsking = null;
289
+ throw error;
290
+ });
291
+ return asking;
292
+ }
293
+
294
+ async function promptForName() {
295
+ const ui = await loadDialogUI();
296
+ return new Promise((resolve) => {
297
+ const theme = ui.resolveTheme();
298
+ const { root, overlay, panel, heading, body, footer, close } = ui.bevelDialog({
256
299
  zIndex: "2147483001", width: "420px", closable: true, titled: true, theme, surface: "people",
257
300
  });
258
301
  askingRoot = root;
@@ -260,27 +303,27 @@ function askName() {
260
303
  heading.textContent = "What name should appear on your changes?";
261
304
  panel.setAttribute("aria-label", "Your name");
262
305
 
263
- const input = bevelInput("input", { rules: ["display:block", "width:100%", "margin:0"], theme, parts: ["dialog.input", "people.input"] });
306
+ const input = ui.bevelInput("input", { rules: ["display:block", "width:100%", "margin:0"], theme, parts: ["dialog.input", "people.input"] });
264
307
  input.setAttribute("aria-label", "Your name");
265
308
  input.maxLength = MAX_NAME;
266
309
  input.autocomplete = "name";
267
- const hint = bevelText("p", ["display:block", "margin:10px 0 0", "font-size:13px", "opacity:.8"],
310
+ const hint = ui.bevelText("p", ["display:block", "margin:10px 0 0", "font-size:13px", "opacity:.8"],
268
311
  "Saved in this browser. Everyone who can read this page will see it.", { theme, parts: ["dialog.hint", "people.hint"], role: "mutedText" });
269
312
  const hintText = hint.textContent;
270
313
  body.append(input, hint);
271
314
 
272
315
  const known = list().slice(0, 8);
273
316
  if (known.length) {
274
- const label = bevelText("p", ["display:block", "margin:18px 0 8px", "font-size:13px"], "Or continue as someone already in this page:", { theme, parts: ["people.choiceLabel"], role: "mutedText" });
275
- const row = bevelBox("div", ["display:flex", "flex-wrap:wrap", "gap:8px"], { theme, parts: ["people.choices"] });
317
+ const label = ui.bevelText("p", ["display:block", "margin:18px 0 8px", "font-size:13px"], "Or continue as someone already in this page:", { theme, parts: ["people.choiceLabel"], role: "mutedText" });
318
+ const row = ui.bevelBox("div", ["display:flex", "flex-wrap:wrap", "gap:8px"], { theme, parts: ["people.choices"] });
276
319
  for (const person of known) {
277
- row.append(bevelButton(person.name, { small: true, onClick: () => finish({ id: person.id, name: person.name }), theme, parts: ["dialog.button", "people.button", "people.choice"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] }));
320
+ row.append(ui.bevelButton(person.name, { small: true, onClick: () => finish({ id: person.id, name: person.name }), theme, parts: ["dialog.button", "people.button", "people.choice"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] }));
278
321
  }
279
322
  body.append(label, row);
280
323
  }
281
324
 
282
- const cancel = bevelButton("Cancel", { onClick: () => finish(null), theme, parts: ["dialog.button", "people.button"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] });
283
- const ok = bevelButton("Continue", { variant: "primary", onClick: () => submit(), theme, parts: ["dialog.button", "people.button"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] });
325
+ const cancel = ui.bevelButton("Cancel", { onClick: () => finish(null), theme, parts: ["dialog.button", "people.button"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] });
326
+ const ok = ui.bevelButton("Continue", { variant: "primary", onClick: () => submit(), theme, parts: ["dialog.button", "people.button"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] });
284
327
  footer.append(cancel, ok);
285
328
 
286
329
  const submit = () => {
@@ -314,7 +357,7 @@ function askName() {
314
357
  const onKey = (event) => {
315
358
  if (event.key === "Tab") {
316
359
  event.stopPropagation();
317
- keepFocusIn(panel, event);
360
+ ui.keepFocusIn(panel, event);
318
361
  return;
319
362
  }
320
363
  if (event.key === "Escape") {
@@ -343,13 +386,12 @@ function askName() {
343
386
  cancelAsking = null;
344
387
  resolve(person);
345
388
  }
346
- dismissOf.set(root, () => finish(null));
389
+ ui.dismissOf.set(root, () => finish(null));
347
390
 
348
391
  root.setAttribute("aria-hidden", "false");
349
392
  modalHost().append(root);
350
393
  input.focus();
351
394
  });
352
- return asking;
353
395
  }
354
396
 
355
397
  // A page's own modal <dialog> makes everything outside it inert, so the prompt goes inside
@@ -367,9 +409,20 @@ function registrySignature() {
367
409
  return JSON.stringify([...saved().values()].map(({ id, name }) => [id, name]));
368
410
  }
369
411
 
412
+ // The observer belongs to the page rather than to one edit session — a reader repaint
413
+ // has to keep the names in it current — so any read starts it and a session that stops
414
+ // takes it down. It is created with the document it watches held once: a test
415
+ // environment can tear that document down while a mutation is still queued, and a
416
+ // callback that reached for the global afterwards would read a page that is gone.
417
+ let watcher = null;
418
+
370
419
  function observe() {
420
+ const doc = document;
421
+ if (watcher?.doc === doc) return;
422
+ watcher?.observer.disconnect();
371
423
  lastRegistry = registrySignature();
372
- new MutationObserver((records) => {
424
+ const observer = new MutationObserver((records) => {
425
+ if (typeof document === "undefined" || document !== doc) return;
373
426
  for (const rec of records) {
374
427
  const target = rec.target.nodeType === 1 ? rec.target : rec.target.parentElement;
375
428
  // A removed clay-people attribute no longer matches the selector, so it is caught by name.
@@ -387,25 +440,99 @@ function observe() {
387
440
  return;
388
441
  }
389
442
  }
390
- }).observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["value", "clay-people"] });
443
+ });
444
+ observer.observe(doc.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["value", "clay-people"] });
445
+ watcher = { doc, observer };
446
+ }
447
+
448
+ function unobserve() {
449
+ watcher?.observer.disconnect();
450
+ watcher = null;
451
+ }
452
+
453
+ // The editing half: the host answer that names me and the two listeners that keep it
454
+ // current, added once for the boot and a lease alike. Each checks the mode when it fires,
455
+ // so a session that has ended asks the host nothing.
456
+ let session = null;
457
+
458
+ function startEditing() {
459
+ if (!editing()) return Promise.resolve(null);
460
+ if (!session) {
461
+ const listeners = new AbortController();
462
+ const { signal } = listeners;
463
+ window.addEventListener("storage", (event) => {
464
+ if (event.key !== STORE || !editing()) return;
465
+ local = undefined;
466
+ emit();
467
+ }, { signal });
468
+ document.addEventListener("visibilitychange", () => {
469
+ if (!editing()) return;
470
+ if (document.visibilityState === "visible" && outcome !== "none") refresh(true);
471
+ }, { signal });
472
+ session = { listeners, first: refresh(false) };
473
+ }
474
+ return Promise.race([session.first, new Promise((resolve) => setTimeout(resolve, BOOT_WAIT_MS))]);
475
+ }
476
+
477
+ function stopEditing() {
478
+ session?.listeners.abort();
479
+ session = null;
391
480
  }
392
481
 
393
482
  async function init() {
394
483
  observe();
395
484
  // A reader is nobody and picks nobody, so a read-only page never asks the host.
396
- if (!editing()) return;
397
- window.addEventListener("storage", (event) => {
398
- if (event.key !== STORE) return;
399
- local = undefined;
400
- emit();
401
- });
402
- document.addEventListener("visibilitychange", () => {
403
- if (document.visibilityState === "visible" && outcome !== "none") refresh(true);
404
- });
405
- const first = refresh(false);
406
- await Promise.race([first, new Promise((resolve) => setTimeout(resolve, BOOT_WAIT_MS))]);
485
+ await startEditing();
486
+ }
487
+
488
+ function peopleLease() {
489
+ return {
490
+ enable() {
491
+ observe();
492
+ startEditing();
493
+ },
494
+ quiesce() {
495
+ if (!asking) return;
496
+ throw Object.assign(new Error("clay:people: a name is still being chosen"), {
497
+ code: "pending-input",
498
+ pending: ["name"],
499
+ });
500
+ },
501
+ resume() {},
502
+ dispose() {
503
+ cancelAsking?.();
504
+ unobserve();
505
+ stopEditing();
506
+ },
507
+ };
407
508
  }
408
509
 
510
+ /**
511
+ * Attribution as a mode lease.
512
+ *
513
+ * Reading names is not editing, so every read keeps the registry observer running and
514
+ * costs a session nothing. What a session adds is a name that is still being chosen: a
515
+ * half-answered prompt is input nobody has committed, so `quiesce` refuses the exit
516
+ * rather than closing it, and `resume` leaves that same prompt on screen. `dispose`
517
+ * (after the barrier) takes back both the prompt and the observer, and the next read
518
+ * starts watching again.
519
+ */
520
+ export function peopleParticipant() {
521
+ return {
522
+ id: "clay:people",
523
+ order: 330,
524
+ prepare(context = {}) {
525
+ const lease = peopleLease();
526
+ // An adoption is the session this module's own import already started: the exit
527
+ // takes it back, and the commit starts nothing a second time.
528
+ if (context.initial !== true) return lease;
529
+ return { ...lease, enable() {} };
530
+ },
531
+ };
532
+ }
533
+
534
+ export { peopleParticipant as modeParticipant };
535
+
409
536
  export const people = Object.freeze({ get, list, available, add, remove, canRename, rename });
410
537
 
411
538
  export const ready = init();
@@ -4,18 +4,128 @@
4
4
  import { installSkin } from "../ui/vendor-skin.js";
5
5
  import { CSS, ROOTS } from "../ui/skins/richclay.js";
6
6
  import { RichClay } from "../vendor/richclay.vendor.js";
7
- import { isEditMode } from "../core/is-edit-mode.js";
7
+ import { startsAtImport } from "../core/is-edit-mode.js";
8
8
 
9
9
  installSkin("richclay", CSS, { roots: ROOTS });
10
10
 
11
- // A frame's snapshot carries none of RichClay's runtime state, and the merge removes
12
- // live attributes the merged page lacks, so an applied frame leaves editors that can
13
- // no longer be typed into. RichClay's reattach() restores them. View mode mounts none.
14
- if (isEditMode) {
15
- document.addEventListener("clay:sync-applied", () => {
16
- for (const editor of RichClay.init()) editor.reattach();
17
- });
11
+ let session = null;
12
+
13
+ const fault = (message, code) => Object.assign(new Error(`clay:richclay: ${message}`), { code });
14
+
15
+ const isStale = (context) => {
16
+ if (!context) return false;
17
+ if (context.signal?.aborted) return true;
18
+ return typeof context.isCurrent === "function" && !context.isCurrent();
19
+ };
20
+
21
+ const ownership = (root) => ({
22
+ root,
23
+ win: root.ownerDocument?.defaultView || window,
24
+ admitted: false,
25
+ stopWatch: null,
26
+ subscriber: null,
27
+ });
28
+
29
+ const elements = (record) => [...record.root.querySelectorAll(RichClay.selector)];
30
+
31
+ // Mounts the regions the page has, and repairs the ones an applied frame stripped: a frame's
32
+ // snapshot carries none of RichClay's runtime state, and the merge removes live attributes
33
+ // the merged page lacks, so an applied frame leaves editors that can no longer be typed
34
+ // into. RichClay's reattach() restores them.
35
+ function mount(record) {
36
+ for (const editor of RichClay.init(elements(record))) editor.reattach();
37
+ }
38
+
39
+ // One edit session over the live DOM. `enable` mounts the page's regions and starts the one
40
+ // watcher for regions that arrive later; `quiesce` closes admission, so a region that
41
+ // arrives while the exit runs is given no editor; `resume` is the refused exit, over the
42
+ // same session; `dispose` destroys every instance and removes exactly the subscription it
43
+ // added, so the view it leaves behind holds no editor and the next entry begins from
44
+ // nothing.
45
+ function richclayLease(record) {
46
+ const onFrame = () => mount(record);
47
+ return {
48
+ enable() {
49
+ if (record.admitted) return;
50
+ record.admitted = true;
51
+ mount(record);
52
+ record.stopWatch = RichClay.watch(record.win);
53
+ const target = record.root.ownerDocument || document;
54
+ record.subscriber = { target, type: "clay:sync-applied", handler: onFrame };
55
+ target.addEventListener(record.subscriber.type, onFrame);
56
+ },
57
+ quiesce() {
58
+ record.admitted = false;
59
+ if (record.stopWatch) {
60
+ record.stopWatch();
61
+ record.stopWatch = null;
62
+ }
63
+ },
64
+ resume() {
65
+ if (record.admitted) return;
66
+ record.admitted = true;
67
+ record.stopWatch = RichClay.watch(record.win);
68
+ },
69
+ dispose() {
70
+ record.admitted = false;
71
+ if (record.stopWatch) {
72
+ record.stopWatch();
73
+ record.stopWatch = null;
74
+ }
75
+ const subscriber = record.subscriber;
76
+ record.subscriber = null;
77
+ if (subscriber) subscriber.target.removeEventListener(subscriber.type, subscriber.handler);
78
+ for (const el of elements(record)) RichClay.stripElement(el);
79
+ if (session === record) session = null;
80
+ },
81
+ };
18
82
  }
19
83
 
20
- export { RichClay };
84
+ /**
85
+ * RichClay as a mode participant: the editors the commit that enters edit mounts, and the
86
+ * ones the exit takes back.
87
+ *
88
+ * `prepare` takes the vendor's auto-watch out of the page first, so every instance is this
89
+ * session's to quiesce and destroy and an entry never meets an editor it did not mount; a
90
+ * vendor build without that lifecycle support is refused here rather than left half
91
+ * mounted in a view. `enable` mounts the regions and starts the session's own watcher.
92
+ * `quiesce` stops admitting regions. `dispose` destroys the instances and hands back the
93
+ * frame subscription, leaving no editor behind for the reader's page.
94
+ */
95
+ export function richclayParticipant(root = document) {
96
+ return {
97
+ id: "clay:richclay",
98
+ order: 310,
99
+ async prepare(context = {}) {
100
+ if (context.initial === true) return adopt();
101
+ if (typeof RichClay.watch !== "function" || typeof RichClay.stripElement !== "function") {
102
+ throw fault("this vendor build has no watchable lifecycle", "unsupported-plugin");
103
+ }
104
+ if (isStale(context)) throw fault("prepared for a stale generation", "stale-document");
105
+ const stopAutoWatch = RichClay.autoInit?.();
106
+ if (typeof stopAutoWatch === "function") stopAutoWatch();
107
+ return richclayLease(session || (session = ownership(root)));
108
+ },
109
+ };
110
+ }
111
+
112
+ // The boot this tab already made, adopted instead of prepared: it is the session `init`
113
+ // below enabled, or, on a boot that started none, nothing to hand back.
114
+ async function adopt() {
115
+ const lease = await ready;
116
+ return { enable() {}, quiesce: () => lease?.quiesce(), resume: () => lease?.resume(), dispose: () => lease?.dispose() };
117
+ }
118
+
119
+ // Auto-init when the module is imported; the loader awaits `ready` before resolving clay.ready
120
+ async function init() {
121
+ if (!startsAtImport()) return null;
122
+ const lease = await richclayParticipant().prepare();
123
+ lease.enable();
124
+ return lease;
125
+ }
126
+
127
+ const ready = init();
128
+
129
+ export { RichClay, init, ready };
130
+ export { richclayParticipant as modeParticipant };
21
131
  export default RichClay;