@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
@@ -399,6 +399,7 @@ function planText(rec, apply) {
399
399
  const subject = rec.recovery.subject;
400
400
  const live = liveOf(subject, apply, subject.nodeType);
401
401
  if (!live) return null;
402
+ const box = live.nodeType === 3 && live.parentNode?.localName === "textarea" ? live.parentNode : live;
402
403
  const local = t.local.text.slice(t.local.start, t.local.end);
403
404
  if (STRUCTURAL.test(t.merged.text.slice(t.merged.start, t.merged.end)) || local.includes(BREAK)) return null;
404
405
  const op = { kind: "text", id: rec.id, ticket: apply.ticket, key: subject.key, fragment: t.local.fragment, encoding: t.encoding, local, ids: new Set(), range: document.createRange() };
@@ -411,12 +412,12 @@ function planText(rec, apply) {
411
412
  }
412
413
  // A textarea shows its value, which typing moves away from its child text: the
413
414
  // spot is found in the value and both are written.
414
- if (live.localName === "textarea") {
415
+ if (box.localName === "textarea") {
415
416
  if (op.encoding !== "plain") return null;
416
- const span = rebase(t.merged.text, live.value, t.merged.start, t.merged.end);
417
+ const span = rebase(t.merged.text, box.value, t.merged.start, t.merged.end);
417
418
  if (!span) return null;
418
- op.range.selectNodeContents(live);
419
- return { ...op, root: live, field: live, scope: live, f: { text: live.value }, s: span.s, e: span.e, before: live.value, bs: span.s, be: span.e };
419
+ op.range.selectNodeContents(box);
420
+ return { ...op, root: box, field: box, scope: box, f: { text: box.value }, s: span.s, e: span.e, before: box.value, bs: span.s, be: span.e };
420
421
  }
421
422
  const f = project(live);
422
423
  const from = scopeStart(t, live, f);
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The edit-only half of the conflict ledger.
3
+ *
4
+ * `conflicts.js` is the one conflict module a reader and an editor both load, and a
5
+ * reader never records a conflict: it has nothing unsaved for an incoming frame to
6
+ * win over. So the copy a reload has to keep, and the store it goes to, live here.
7
+ * `conflicts.js` starts this fetch as it evaluates in edit mode, and hands it the
8
+ * material it cannot rebuild; a conflict that arrives before it lands is queued
9
+ * there and drains the moment it does.
10
+ */
11
+
12
+ import { TAB_LOCAL_ROOT_ATTRS } from '../lib/root-attrs.js';
13
+ import { serializeRecoveryClone } from '../core/snapshot.js';
14
+ import { rowOf } from '../core/conflict-presentation.js';
15
+
16
+ // The recovery half of this module is fetched, not imported: no recovery code is in
17
+ // the boot graph, and a page that never loses anything never downloads it. The copy
18
+ // itself is made synchronously below and queued here, so what has to be preserved is
19
+ // preserved before anything is released; the queue drains the moment the module
20
+ // arrives. Until it does, the live record is the only copy, which is where this
21
+ // started.
22
+ const DURABLE_QUEUE_MAX = 20;
23
+ let durable = null;
24
+ let loadingDurable = false;
25
+ const durableQueue = [];
26
+
27
+ function loadDurable() {
28
+ if (durable || loadingDurable) return;
29
+ loadingDurable = true;
30
+ import('../recovery/conflicts.js').then(
31
+ (module) => {
32
+ durable = module;
33
+ loadingDurable = false;
34
+ drainDurable();
35
+ },
36
+ (error) => {
37
+ loadingDurable = false;
38
+ console.error('clayjs: the conflict recovery module could not be downloaded; conflicts stay in this tab only', error);
39
+ }
40
+ );
41
+ }
42
+
43
+ function drainDurable() {
44
+ if (!durable) {
45
+ loadDurable();
46
+ return;
47
+ }
48
+ while (durableQueue.length) {
49
+ const spec = durableQueue.shift();
50
+ try {
51
+ durable.recordConflicts(spec);
52
+ } catch (err) {
53
+ console.error('clayjs: the conflict could not be stored for a later reload; it is still here', err);
54
+ }
55
+ }
56
+ }
57
+
58
+ // The losing text a reload has to keep, as the notice already words it. Everything
59
+ // here is plain text: the row's own name, the sentence that explains it, and the two
60
+ // sides, with the rest of the copy in the stored root.
61
+ function conflictEntry(rec, context) {
62
+ let row = null;
63
+ try {
64
+ row = rowOf(rec);
65
+ } catch (_) {}
66
+ const full = (side) => (!side ? '' : (side.cutBefore ? '\u2026' : '') + side.before + side.hit + side.after + (side.cutAfter ? '\u2026' : ''));
67
+ return {
68
+ id: String(rec.id),
69
+ kind: String(rec.kind ?? ''),
70
+ key: rec.recovery?.key == null ? '' : String(rec.recovery.key),
71
+ name: row?.name ? String(row.name) : '',
72
+ sentence: row?.sentence ? String(row.sentence) : '',
73
+ yours: full(row?.yours),
74
+ now: full(row?.now),
75
+ context,
76
+ };
77
+ }
78
+
79
+ // The pre-apply root as the bytes a save would have written, with the root
80
+ // attributes this response and this tab own removed first: a recovery copy never
81
+ // carries a save token, a file id or this library's own UI state.
82
+ function recoveryHtml(apply) {
83
+ try {
84
+ const source = apply.recoveryRoot ?? apply.root;
85
+ const clone = source?.cloneNode?.(true);
86
+ if (!clone) return null;
87
+ for (const name of TAB_LOCAL_ROOT_ATTRS) clone.removeAttribute(name);
88
+ return serializeRecoveryClone(clone, { prepared: apply.domain === 'save' });
89
+ } catch (err) {
90
+ console.error('clayjs: the conflict recovery page could not be serialized; the conflict text is still stored', err);
91
+ return null;
92
+ }
93
+ }
94
+
95
+ // Copy the material before anything is released, and never let the store's failure
96
+ // reach the ledger: a quota or a blocked database leaves the live record exactly
97
+ // where it was. Losing the copy to this queue is bounded, never silent.
98
+ export function keep({ apply, records }) {
99
+ try {
100
+ durableQueue.push({
101
+ apply: { id: apply.id, source: apply.source, domain: apply.domain, seq: apply.seq, etag: apply.etag, ticket: apply.ticket },
102
+ root: recoveryHtml(apply),
103
+ conflicts: records.map(({ rec, context }) => conflictEntry(rec, context)),
104
+ });
105
+ if (durableQueue.length > DURABLE_QUEUE_MAX) durableQueue.shift();
106
+ drainDurable();
107
+ } catch (err) {
108
+ console.error('clayjs: the conflict could not be stored for a later reload; it is still here', err);
109
+ }
110
+ }
111
+
112
+ // An acknowledged conflict leaves the store with the ledger record it came from:
113
+ // nothing here reads it back, and a store that cannot be reached just keeps it.
114
+ export function dismiss(ids) {
115
+ import('../recovery/conflicts.js').then(
116
+ (module) => module.dismissConflicts(ids),
117
+ () => {}
118
+ ).catch(() => {});
119
+ }
@@ -0,0 +1,49 @@
1
+ // clay.conflicts before the conflict ledger has loaded. No merge has happened, so
2
+ // there are no conflicts: reads answer empty, writes have nothing to act on, and a
3
+ // context resolver registered now is handed to the ledger when it loads. Once loaded,
4
+ // every member delegates, so the object a page holds never changes.
5
+ let ledger = null;
6
+ let loading = null;
7
+ const resolvers = new Map();
8
+
9
+ export function loadConflicts() {
10
+ loading ??= Promise.all([import('./conflicts.js'), import('./conflict-footprints.js')]).then(([c, f]) => {
11
+ ledger = { ...c, beginLineageCapture: f.beginLineageCapture };
12
+ // The unregister the real registry handed back, so a resolver the page drops
13
+ // before or after the download leaves the ledger's registry alike.
14
+ for (const resolver of [...resolvers.keys()]) resolvers.set(resolver, c.registerContext(resolver));
15
+ return ledger;
16
+ });
17
+ return loading;
18
+ }
19
+
20
+ export const conflictLedger = () => ledger;
21
+
22
+ export const conflicts = {
23
+ get size() { return ledger ? ledger.conflicts.size : 0; },
24
+ list() { return ledger ? ledger.conflicts.list() : []; },
25
+ get(id) { return ledger ? ledger.conflicts.get(id) : null; },
26
+ acknowledge(ids, options = {}) {
27
+ if (ledger) return ledger.conflicts.acknowledge(ids, options);
28
+ if (!Array.isArray(ids)) throw new TypeError('clay.conflicts.acknowledge needs an array of ids');
29
+ return { removedIds: [], remainingIds: [] };
30
+ },
31
+ claim(ids, options = {}) {
32
+ if (ledger) return ledger.conflicts.claim(ids, options);
33
+ if (!options.owner) throw new TypeError('clay.conflicts.claim needs an owner');
34
+ return { release() {} };
35
+ },
36
+ registerContext(resolver) {
37
+ if (ledger) return ledger.conflicts.registerContext(resolver);
38
+ if (typeof resolver !== 'function') throw new TypeError('clay.conflicts.registerContext needs a resolver function');
39
+ resolvers.set(resolver, null);
40
+ return () => {
41
+ const registered = resolvers.get(resolver);
42
+ resolvers.delete(resolver);
43
+ if (registered) registered();
44
+ };
45
+ },
46
+ contextFor(rec, apply) { return ledger ? ledger.conflicts.contextFor(rec, apply) : null; },
47
+ recoveryOf(id) { return ledger ? ledger.conflicts.recoveryOf(id) : null; },
48
+ hasPendingApply() { return ledger ? ledger.conflicts.hasPendingApply() : false; },
49
+ };
@@ -1,5 +1,6 @@
1
1
  import { registerUnsavedState } from '../lib/unsaved-state.js';
2
2
  import { releaseProtection } from './conflict-footprints.js';
3
+ import { isEditMode } from '../core/is-edit-mode.js';
3
4
 
4
5
  // What incoming frames won over this tab's unsaved edits, kept for the life of
5
6
  // the document until the person decides. Nothing here is read by a save, a
@@ -13,8 +14,32 @@ let counter = 0;
13
14
  const records = new Map();
14
15
  const applies = new Map();
15
16
  const pending = new Set();
17
+ const resolvers = new Set();
16
18
  let registration = null;
17
19
 
20
+ // The recording half is edit-only, like live sync's: a reader has nothing unsaved to
21
+ // lose. Edit mode starts the fetch as this file evaluates; a loss that arrives first
22
+ // waits in the queue below.
23
+ const EDIT_QUEUE_MAX = 20;
24
+ let edit = null;
25
+ let editLoad = null;
26
+ const editQueue = [];
27
+
28
+ export function loadEdit() {
29
+ editLoad ??= import('./conflicts-edit.js').then(
30
+ (module) => {
31
+ edit = module;
32
+ while (editQueue.length) edit.keep(editQueue.shift());
33
+ },
34
+ (error) => {
35
+ editLoad = null;
36
+ console.error('clayjs: the conflict recovery module could not be downloaded; conflicts stay in this tab only', error);
37
+ }
38
+ );
39
+ }
40
+
41
+ if (isEditMode) loadEdit();
42
+
18
43
  function nextId(prefix = '') {
19
44
  return `${nonce}:${prefix}${++counter}`;
20
45
  }
@@ -45,12 +70,61 @@ function isLoss(c) {
45
70
  return r.localLost !== false;
46
71
  }
47
72
 
73
+ // The page names a conflict when it can: a record id, a label and a field, from the
74
+ // app's own registered resolver. What comes back is a name and an action for the
75
+ // notice, never a claim, and never a replacement for the generic ones.
76
+ const CONTEXT_FIELDS = ['kind', 'id', 'label', 'field'];
77
+
78
+ // What can be stored: the identity, the label and the field. An `open` action is a
79
+ // function bound to this page's tree and stays on the live record.
80
+ function contextFields(value) {
81
+ if (!value || typeof value !== 'object') return null;
82
+ const context = {};
83
+ for (const field of CONTEXT_FIELDS) {
84
+ const found = value[field];
85
+ if (typeof found === 'string' && found) context[field] = found;
86
+ else if (typeof found === 'number' && Number.isFinite(found)) context[field] = String(found);
87
+ }
88
+ return Object.keys(context).length ? context : null;
89
+ }
90
+
91
+ function contextOf(value) {
92
+ const context = contextFields(value);
93
+ if (!context) return null;
94
+ if (typeof value.open === 'function') context.open = value.open;
95
+ return context;
96
+ }
97
+
98
+ // The newest registration answers first: an app that changes its own resolver gets
99
+ // the new one without having to unregister the old.
100
+ function contextFor(rec, apply) {
101
+ for (const resolver of [...resolvers].reverse()) {
102
+ let answer;
103
+ try {
104
+ answer = resolver(rec, apply);
105
+ } catch (err) {
106
+ console.error('clayjs: a conflict context resolver threw; the conflict stays generic', err);
107
+ continue;
108
+ }
109
+ const context = contextOf(answer);
110
+ if (context) return context;
111
+ }
112
+ return null;
113
+ }
114
+
115
+ export function registerContext(resolver) {
116
+ if (typeof resolver !== 'function') throw new TypeError('clay.conflicts.registerContext needs a resolver function');
117
+ resolvers.add(resolver);
118
+ return () => resolvers.delete(resolver);
119
+ }
120
+
48
121
  function install(apply, raw) {
49
122
  const id = nextId();
50
123
  const rec = Object.assign(raw[0], {
51
124
  id, applyId: apply.id, source: apply.source, ticket: apply.ticket,
52
125
  rawReports: raw, claimedBy: null,
53
126
  });
127
+ rec.context = contextFor(rec, apply);
54
128
  records.set(id, rec);
55
129
  apply.ids.add(id);
56
130
  return id;
@@ -91,6 +165,19 @@ function foldInsertions(groups) {
91
165
  return [...folded.values()];
92
166
  }
93
167
 
168
+ // What the store may keep of a record's context, read now, while the record still
169
+ // knows it: the identity, the label and the field, never the resolver's action, which
170
+ // is a function bound to this page's tree and stays on the live record.
171
+ function keepConflicts(apply, ids) {
172
+ const kept = ids.map((id) => records.get(id)).filter(Boolean);
173
+ if (!kept.length) return;
174
+ const spec = { apply, records: kept.map((rec) => ({ rec, context: contextFields(rec.context) })) };
175
+ if (edit) return edit.keep(spec);
176
+ editQueue.push(spec);
177
+ if (editQueue.length > EDIT_QUEUE_MAX) editQueue.shift();
178
+ loadEdit();
179
+ }
180
+
94
181
  export function completeApply(applyId, conflicts, { ticket }) {
95
182
  const apply = applies.get(applyId);
96
183
  if (!apply) return [];
@@ -103,6 +190,7 @@ export function completeApply(applyId, conflicts, { ticket }) {
103
190
  groups.get(key).push(c);
104
191
  }
105
192
  const ids = foldInsertions([...groups.values()]).map((raw) => install(apply, raw));
193
+ if (ids.length) keepConflicts(apply, ids);
106
194
  pending.delete(applyId);
107
195
  if (!ids.length) applies.delete(applyId);
108
196
  if (ids.length) changed({ added: ids.map((id) => records.get(id)) });
@@ -117,6 +205,7 @@ export function failApply(applyId, error, { ticket = 0 } = {}) {
117
205
  pending.delete(applyId);
118
206
  const id = install(apply, [{ kind: 'apply-incomplete', detail: null, error: String(error) }]);
119
207
  records.get(id).rawReports = [];
208
+ keepConflicts(apply, [id]);
120
209
  changed({ added: [records.get(id)] });
121
210
  return [id];
122
211
  }
@@ -147,7 +236,13 @@ export const conflicts = {
147
236
  if (!apply.ids.size) applies.delete(rec.applyId);
148
237
  }
149
238
  }
150
- if (removedIds.length) changed({ removedIds, reason });
239
+ if (removedIds.length) {
240
+ changed({ removedIds, reason });
241
+ import('./conflicts-edit.js').then(
242
+ (module) => module.dismiss(removedIds),
243
+ () => {}
244
+ );
245
+ }
151
246
  return { removedIds, remainingIds: [...records.keys()] };
152
247
  },
153
248
  claim(ids, { owner }) {
@@ -178,6 +273,9 @@ export const conflicts = {
178
273
  },
179
274
  };
180
275
  },
276
+ registerContext,
277
+ contextFor,
278
+
181
279
  recoveryOf(id) {
182
280
  const rec = records.get(id);
183
281
  return rec ? applies.get(rec.applyId) || null : null;
@@ -14,18 +14,37 @@
14
14
  import { hostSupports } from '../core/host-meta.js';
15
15
  import { make, set } from '../lib/hostile-css.js';
16
16
  import { personColor } from '../lib/person-face.js';
17
- import { RUNTIME_ONLY } from '../ui/bevel-controls.js';
18
- import { FONT_MONO } from '../ui/bevel.js';
19
17
  import { EDITING_PATH, elementAt } from './editing-path.js';
20
18
 
21
19
  const FACE = '#FFFCF6';
22
20
  const RESET = ['all:initial', 'color-scheme:inherit', 'direction:ltr', 'unicode-bidi:isolate'];
23
21
  const CHIP_HEIGHT = 18;
24
22
  const GAP = 3;
25
- const CHIP_FONT = `600 11px/16px ${FONT_MONO}`;
23
+
24
+ // The marking token and the chip face are the Bevel subset, a first-use download: a
25
+ // reader with nobody else on the page never draws a marker, so it never fetches them.
26
+ let ui = null;
27
+ let uiLoad = null;
28
+
29
+ function loadUI() {
30
+ uiLoad ??= Promise.all([import('../ui/bevel-controls.js'), import('../ui/bevel.js')])
31
+ .then(([controls, bevel]) => (ui = {
32
+ RUNTIME_ONLY: controls.RUNTIME_ONLY,
33
+ CHIP_FONT: `600 11px/16px ${bevel.FONT_MONO}`,
34
+ }))
35
+ .catch((error) => {
36
+ uiLoad = null;
37
+ throw error;
38
+ });
39
+ return uiLoad;
40
+ }
41
+
42
+ // A frame never rejects its way back to the stream: a chunk that failed to arrive
43
+ // leaves the block unmarked and the next frame asks again.
44
+ const uiReady = () => loadUI().then(() => true, () => false);
26
45
 
27
46
  function runtimeRoot(el) {
28
- el.setAttribute('clay', RUNTIME_ONLY);
47
+ el.setAttribute('clay', ui.RUNTIME_ONLY);
29
48
  return el;
30
49
  }
31
50
 
@@ -59,7 +78,7 @@ class EditingMarker {
59
78
  async update(frame) {
60
79
  this.editors = editorsOf(frame);
61
80
  if (!(await this.supported())) return;
62
- this.render();
81
+ await this.render();
63
82
  }
64
83
 
65
84
  clear() {
@@ -79,7 +98,19 @@ class EditingMarker {
79
98
  });
80
99
  }
81
100
 
101
+ // A synchronous draw while the chunk is here, as it always was, and one draw
102
+ // queued for when it arrives on the first frame that needs it.
82
103
  render() {
104
+ if (ui) {
105
+ this.paint();
106
+ return Promise.resolve();
107
+ }
108
+ return uiReady().then((ok) => {
109
+ if (ok) this.paint();
110
+ });
111
+ }
112
+
113
+ paint() {
83
114
  const groups = new Map();
84
115
  for (const editor of this.editors) {
85
116
  const element = elementAt(editor.path);
@@ -130,7 +161,7 @@ class EditingMarker {
130
161
  const chip = runtimeRoot(make('span', [
131
162
  ...RESET,
132
163
  'box-sizing:border-box', `height:${CHIP_HEIGHT}px`, 'padding:1px 6px',
133
- `font:${CHIP_FONT}`, 'letter-spacing:.02em', 'white-space:nowrap',
164
+ `font:${ui.CHIP_FONT}`, 'letter-spacing:.02em', 'white-space:nowrap',
134
165
  'max-width:180px', 'overflow:hidden', 'text-overflow:ellipsis',
135
166
  `background-color:${personColor(editor.id)}`, `color:${FACE}`,
136
167
  `box-shadow:0 0 0 1px ${FACE}`,
@@ -1,11 +1,18 @@
1
1
  import { blockOf, pathOf } from './editing-path.js';
2
+ import { presence } from './presence-store.js';
2
3
 
3
4
  const INTERVAL_MS = 250;
4
5
  const RETRY_MS = 2000;
5
6
  const EVENTS = ['focusin', 'focusout', 'selectionchange', 'clay:sync-applied', 'clay:workspace-background', 'clay:workspace-foreground'];
6
7
 
8
+ // The default is the facade's generic half: a page that publishes a target does
9
+ // not post it itself, and an owner that attached a transport is the only thing that
10
+ // does. The marker stands aside while an explicit page target owns the session, so
11
+ // one document never has two senders talking at once.
12
+ const publish = (target) => presence.setGeneric(target);
13
+
7
14
  export class EditingSender {
8
- constructor({ send, now = () => Date.now() }) {
15
+ constructor({ send = publish, now = () => Date.now() }) {
9
16
  this.send = send;
10
17
  this.now = now;
11
18
  this.others = false;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The edit-only half of live sync.
3
+ *
4
+ * `live-sync.js` is the one sync module a reader and an editor both load. The
5
+ * names below are the ones it reaches only in edit mode (or whose view-mode
6
+ * answer is a constant), so they live in this separate chunk: live-sync loads it
7
+ * with `await import()` on its edit path, and a view-mode tab never fetches the
8
+ * save, admin or editing-sender graph behind it.
9
+ */
10
+ import { loadEdit as loadConflictsEdit } from "./conflicts.js";
11
+ import { presence } from "./presence-store.js";
12
+
13
+ export { savePageThrottled, setLastSavedBaselines, setUnsavedChanges, getLastSavedBytes, conflictResolvedBySync, isSaveConflicted, baselineSettled, schedulePaintHeal } from "../core/save.js";
14
+ export { enableContentEditable } from "../core/admin-contenteditable.js";
15
+ export { enableOnClick } from "../core/admin-onclick.js";
16
+ export { enableAdminInputs } from "../core/admin-inputs.js";
17
+ export { enableAdminResources } from "../core/admin-resources.js";
18
+ export { EditingSender } from "./editing-sender.js";
19
+
20
+ // The lifecycle participant a live switch prepares for generic sync. `sync` is the
21
+ // one LiveSync instance the page owns: the saved lane it follows as a reader is what
22
+ // an entry stops, and the live lane it starts is what an exit disposes. The exit's
23
+ // saved lane is the controller's to restart, not this lease's.
24
+ export function syncParticipant(sync) {
25
+ // The outgoing target goes before the editor is taken down, so a caret this tab
26
+ // published cannot outlive the session that published it. A page target is
27
+ // released with the ownership it came with; the generic marker is cleared
28
+ // outright. Neither falls back to a positional marker: the exit publishes nobody.
29
+ const quiesce = () => {
30
+ presence.setGeneric(null);
31
+ presence.releaseTarget();
32
+ return sync.lane === "live" && sync._morphInFlight ? { ok: false, code: "pending-input" } : { ok: true };
33
+ };
34
+ const dispose = () => sync.stop();
35
+ return {
36
+ id: "clay:sync",
37
+ order: 50,
38
+ async prepare(context) {
39
+ await loadConflictsEdit();
40
+ // Adoption: the explicit initial edit started this lane as the module
41
+ // evaluated, so the lease covers it and starts nothing new.
42
+ if (context?.initial) return { quiesce, dispose };
43
+ let started = false;
44
+ return {
45
+ quiesce,
46
+ // The commit: the lane a reader was following stops, and the same stream
47
+ // slot opens on the live lane.
48
+ enable() {
49
+ const file = sync.currentFile;
50
+ sync.stop();
51
+ sync.start(file, { lane: "live" });
52
+ started = true;
53
+ },
54
+ // A refused entry disposes what it prepared as well, and until the commit this
55
+ // lane was the reader's: stopping it would take the saved lane the switch gives
56
+ // back with it.
57
+ dispose: () => {
58
+ if (started) sync.stop();
59
+ },
60
+ };
61
+ },
62
+ };
63
+ }