@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
@@ -0,0 +1,306 @@
1
+ // Projecting an accepted saved file onto the article a reader is showing.
2
+ //
3
+ // The saved markup is already the rendered form of the writer's model, so this is
4
+ // not a second merge engine: it never combines concurrent authored changes. It only
5
+ // brings a read-only article up to one accepted file while keeping the nodes that
6
+ // file did not change, so a mounted tool, an open toggle and find-in-page survive.
7
+ //
8
+ // Identity comes from logical block `data-id` values. Lists and toggle bodies are
9
+ // walked; boards, tables, charts, media and tools are opaque interiors, so a board
10
+ // card id can never match an article block id. An id that appears twice is not
11
+ // reused at all: neither occurrence promises node identity.
12
+
13
+ const ARTICLE = "article[data-page]";
14
+ const TOOL = 'section[data-type="tool"][data-id]';
15
+ const CONTAINERS = new Set(["UL", "OL", "LI", "DETAILS"]);
16
+ const LISTS = new Set(["UL", "OL"]);
17
+
18
+ export const isTool = (el) => el.tagName === "SECTION" && el.getAttribute("data-type") === "tool" && el.hasAttribute("data-id");
19
+
20
+ /**
21
+ * The tools an article holds, keyed by id, with the revision and artifact their
22
+ * root names. A decode failure is kept as `invalid` so a reader can say so and
23
+ * leave the saved fallback markup visible instead of mounting garbage.
24
+ */
25
+ export function toolInfoOf(el) {
26
+ return toolInfo(el);
27
+ }
28
+
29
+ function toolInfo(el) {
30
+ const data = el.querySelector(':scope > script[type="application/json"][data-tool]');
31
+ let parsed = {};
32
+ try {
33
+ parsed = data ? JSON.parse(data.textContent) : {};
34
+ } catch (error) {
35
+ return { rev: null, artifact: null, state: {}, signature: null, invalid: true, error };
36
+ }
37
+ return {
38
+ rev: parsed.rev ?? null,
39
+ artifact: parsed.artifact ?? null,
40
+ state: parsed.state ?? {},
41
+ signature: JSON.stringify(parsed.artifact ?? null),
42
+ invalid: false,
43
+ };
44
+ }
45
+
46
+ export function toolsOf(root) {
47
+ const tools = new Map();
48
+ const duplicated = new Set();
49
+ for (const el of root.querySelectorAll(TOOL)) {
50
+ const id = el.getAttribute("data-id");
51
+ if (duplicated.has(id)) continue;
52
+ if (tools.has(id)) {
53
+ tools.delete(id);
54
+ duplicated.add(id);
55
+ continue;
56
+ }
57
+ tools.set(id, toolInfo(el));
58
+ }
59
+ return tools;
60
+ }
61
+
62
+ export function toolStateOf(doc) {
63
+ const tag = doc.querySelector('script[type="application/json"][merge="page.tools"]');
64
+ if (!tag) return {};
65
+ try { return JSON.parse(tag.textContent); } catch { return {}; }
66
+ }
67
+
68
+ function indexBlocks(root) {
69
+ const found = new Map();
70
+ const duplicated = new Set();
71
+ const walk = (container) => {
72
+ for (const child of container.children) {
73
+ const id = child.getAttribute("data-id");
74
+ if (id) {
75
+ if (duplicated.has(id)) continue;
76
+ if (found.has(id)) {
77
+ found.delete(id);
78
+ duplicated.add(id);
79
+ continue;
80
+ }
81
+ found.set(id, child);
82
+ }
83
+ if (CONTAINERS.has(child.tagName)) walk(child);
84
+ }
85
+ };
86
+ walk(root);
87
+ return found;
88
+ }
89
+
90
+ function retainedIds(root) {
91
+ const ids = new Set();
92
+ const walk = (container) => {
93
+ for (const child of container.children) {
94
+ const id = child.getAttribute("data-id");
95
+ if (id) ids.add(id);
96
+ if (CONTAINERS.has(child.tagName)) walk(child);
97
+ }
98
+ };
99
+ walk(root);
100
+ return ids;
101
+ }
102
+
103
+ /**
104
+ * Validate one full saved file before anything is replaced. Exactly one page
105
+ * article must be present, and the bytes must be the string the host sent, not a
106
+ * live DOM snapshot.
107
+ */
108
+ export function prepareSavedArticle(html, doc = document) {
109
+ if (typeof html !== "string" || html === "") return { ok: false, reason: "bytes" };
110
+ const parser = (doc.defaultView ?? globalThis).DOMParser;
111
+ if (typeof parser !== "function") return { ok: false, reason: "parser" };
112
+ const source = new parser().parseFromString(html, "text/html");
113
+ const articles = [...source.querySelectorAll(ARTICLE)];
114
+ if (articles.length !== 1) return { ok: false, reason: "article" };
115
+ const article = articles[0];
116
+ const savedOpen = new Map();
117
+ for (const details of article.querySelectorAll("details[data-id]")) {
118
+ savedOpen.set(details.getAttribute("data-id"), details.hasAttribute("open"));
119
+ }
120
+ const tools = toolsOf(article);
121
+ return {
122
+ ok: true,
123
+ article,
124
+ source,
125
+ rev: article.getAttribute("data-rev"),
126
+ title: source.title || null,
127
+ savedOpen,
128
+ tools,
129
+ toolState: tools.size ? toolStateOf(source) : {},
130
+ };
131
+ }
132
+
133
+ export const keepToolVersion = (live, incoming) => {
134
+ const a = toolInfo(live);
135
+ const b = toolInfo(incoming);
136
+ return !a.invalid && !b.invalid && a.rev === b.rev && a.signature === b.signature;
137
+ };
138
+
139
+ /**
140
+ * Apply one prepared file to the live article. Returns whether anything changed.
141
+ *
142
+ * Each container's children are reconciled in saved order: an equal keyed block is
143
+ * reused, a compatible keyed block is moved rather than cloned, a changed ordinary
144
+ * leaf is replaced, and a changed list or toggle is recursed into so its unchanged
145
+ * descendants survive. A tool whose id, revision and artifact still match is left
146
+ * untouched: its runtime subtree and reader-local interaction state are the host's.
147
+ */
148
+ export function applyPreparedArticle(prepared, { article, readerOpen = new Map(), savedOpen = null, keepTool = keepToolVersion } = {}) {
149
+ if (!prepared?.ok || !article) return { changed: false };
150
+ for (const details of prepared.article.querySelectorAll("details[data-id]")) {
151
+ const id = details.getAttribute("data-id");
152
+ if (!readerOpen.has(id)) continue;
153
+ if (details.hasAttribute("open") !== readerOpen.get(id)) details.toggleAttribute("open", readerOpen.get(id));
154
+ }
155
+ const index = indexBlocks(article);
156
+ const used = new Set();
157
+ let changed = false;
158
+
159
+ const fresh = (incoming) => {
160
+ changed = true;
161
+ return incoming.cloneNode(true);
162
+ };
163
+
164
+ const matchingUnkeyed = (incoming, container) => {
165
+ const wanted = retainedIds(incoming);
166
+ for (const live of container.children) {
167
+ if (used.has(live) || live.hasAttribute("data-id")) continue;
168
+ if (live.tagName !== incoming.tagName) continue;
169
+ if (!wanted.size) return live;
170
+ const have = retainedIds(live);
171
+ for (const id of wanted) if (have.has(id)) return live;
172
+ }
173
+ return null;
174
+ };
175
+
176
+ const matchingLeaf = (incoming, container) => {
177
+ for (const live of container.children) {
178
+ if (used.has(live) || live.hasAttribute("data-id")) continue;
179
+ if (live.tagName === incoming.tagName) return live;
180
+ }
181
+ return null;
182
+ };
183
+
184
+ const syncAttributes = (live, incoming, skip = null) => {
185
+ for (const { name, value } of [...incoming.attributes]) {
186
+ if (name === skip || live.getAttribute(name) === value) continue;
187
+ live.setAttribute(name, value);
188
+ changed = true;
189
+ }
190
+ for (const { name } of [...live.attributes]) {
191
+ if (name === skip || incoming.hasAttribute(name)) continue;
192
+ live.removeAttribute(name);
193
+ changed = true;
194
+ }
195
+ };
196
+
197
+ const reuseNode = (incoming, live, at) => {
198
+ const same = (candidate) =>
199
+ candidate && candidate.nodeType === incoming.nodeType && candidate.nodeValue === incoming.nodeValue && !used.has(candidate);
200
+ const found = same(live.childNodes[at]) ? live.childNodes[at] : [...live.childNodes].find(same);
201
+ if (!found) return incoming.cloneNode(true);
202
+ used.add(found);
203
+ return found;
204
+ };
205
+
206
+ const reconcileContainer = (live, incoming) => {
207
+ const ordered = [];
208
+ const children = [...incoming.childNodes];
209
+ for (let i = 0; i < children.length; i++) {
210
+ const child = children[i];
211
+ if (child.nodeType !== 1) {
212
+ ordered.push(reuseNode(child, live, i));
213
+ continue;
214
+ }
215
+ const node = pick(child, live);
216
+ used.add(node);
217
+ ordered.push(node);
218
+ }
219
+ const kept = new Set(ordered);
220
+ let walk = live.firstChild;
221
+ for (const node of ordered) {
222
+ while (walk && !kept.has(walk)) walk = walk.nextSibling;
223
+ if (walk === node) {
224
+ walk = walk.nextSibling;
225
+ continue;
226
+ }
227
+ live.insertBefore(node, walk);
228
+ changed = true;
229
+ }
230
+ for (const child of [...live.childNodes]) {
231
+ if (kept.has(child)) continue;
232
+ child.remove();
233
+ changed = true;
234
+ }
235
+ };
236
+
237
+ const freshShell = (incoming) => {
238
+ changed = true;
239
+ const node = incoming.cloneNode(false);
240
+ reconcileContainer(node, incoming);
241
+ return node;
242
+ };
243
+
244
+ const reconcileDetails = (live, incoming) => {
245
+ changed = true;
246
+ const id = live.getAttribute("data-id");
247
+ const open = readerOpen.has(id) ? readerOpen.get(id) : incoming.hasAttribute("open");
248
+ syncAttributes(live, incoming, "open");
249
+ if (live.hasAttribute("open") !== open) live.toggleAttribute("open", open);
250
+ reconcileContainer(live, incoming);
251
+ };
252
+
253
+ const reconcileItem = (live, incoming) => {
254
+ changed = true;
255
+ syncAttributes(live, incoming);
256
+ reconcileContainer(live, incoming);
257
+ };
258
+
259
+ function pick(incoming, container) {
260
+ const id = incoming.getAttribute("data-id");
261
+ if (id) {
262
+ const live = index.get(id);
263
+ if (!live || used.has(live) || live === container || live.contains(container)) return fresh(incoming);
264
+ used.add(live);
265
+ if (isTool(live) && isTool(incoming)) return keepTool(live, incoming) ? live : fresh(incoming);
266
+ if (live.tagName !== incoming.tagName) return fresh(incoming);
267
+ if (live.isEqualNode(incoming)) return live;
268
+ if (incoming.tagName === "DETAILS") {
269
+ reconcileDetails(live, incoming);
270
+ return live;
271
+ }
272
+ if (incoming.tagName === "LI") {
273
+ reconcileItem(live, incoming);
274
+ return live;
275
+ }
276
+ if (LISTS.has(incoming.tagName)) {
277
+ syncAttributes(live, incoming);
278
+ reconcileContainer(live, incoming);
279
+ return live;
280
+ }
281
+ return fresh(incoming);
282
+ }
283
+ if (LISTS.has(incoming.tagName)) {
284
+ const live = matchingUnkeyed(incoming, container);
285
+ if (!live) return freshShell(incoming);
286
+ used.add(live);
287
+ syncAttributes(live, incoming);
288
+ reconcileContainer(live, incoming);
289
+ return live;
290
+ }
291
+ const live = matchingLeaf(incoming, container);
292
+ if (!live) return fresh(incoming);
293
+ used.add(live);
294
+ if (live.isEqualNode(incoming)) return live;
295
+ changed = true;
296
+ return fresh(incoming);
297
+ }
298
+
299
+ reconcileContainer(article, prepared.article);
300
+ if (savedOpen) {
301
+ savedOpen.clear();
302
+ for (const [id, open] of prepared.savedOpen) savedOpen.set(id, open);
303
+ }
304
+ for (const id of [...readerOpen.keys()]) if (!prepared.savedOpen.has(id)) readerOpen.delete(id);
305
+ return { changed };
306
+ }
@@ -1,4 +1,4 @@
1
- import toast, { toastPersistent, dismissPersistent } from "../ui/toast.js";
1
+ import toast, { toastPersistent, dismissPersistent } from "../ui/toast-lazy.js";
2
2
  import { state as pmState } from "../vendor/prosemirror.vendor.js";
3
3
 
4
4
  export const NOTICE_TEXT = "Some text you typed was kept aside when its block moved or was removed.";
@@ -1,5 +1,6 @@
1
1
  import { cloneJson } from "../../live/ops.js";
2
2
  import { validateArtifact, validateState } from "./tool-runtime.js";
3
+ export { capabilityAdvertised } from "../tool-capability.js";
3
4
 
4
5
  const CONTEXT_LIMIT = 16384;
5
6
  const bytes = (value) => new TextEncoder().encode(JSON.stringify(value)).length;
@@ -30,10 +31,6 @@ function trimmedContext(context) {
30
31
  return copy;
31
32
  }
32
33
 
33
- export function capabilityAdvertised(meta) {
34
- return Array.isArray(meta?.extensions) && meta.extensions.includes("page-tool/1");
35
- }
36
-
37
34
  export function createToolClient({ transport, newId }) {
38
35
  let active = null;
39
36
  const discard = (candidate) => {
@@ -0,0 +1,27 @@
1
+ // One runtime owns an article at a time. A reader owns the article it projects;
2
+ // the editor owns it from the moment it mounts, including the noneditable period
3
+ // while saved feature views are still arriving. A mode flag is not enough: two
4
+ // runtimes both think they may write, and a late frame lands over a mounted tool.
5
+ //
6
+ // The kind is diagnostic, not permission to share. A second claim on the same
7
+ // article throws even when it names the same kind, so a double start fails loudly
8
+ // instead of leaving two listeners on one article.
9
+
10
+ const owners = new WeakMap();
11
+
12
+ export function claim(article, kind) {
13
+ if (!article || article.nodeType !== 1) throw new TypeError("clayjs: claim needs an element to own");
14
+ if (owners.has(article)) throw new Error(`clayjs: the article already has a ${owners.get(article).kind} runtime`);
15
+ const token = { kind: String(kind ?? "runtime"), active: true };
16
+ owners.set(article, token);
17
+ return function release() {
18
+ if (!token.active) return false;
19
+ token.active = false;
20
+ if (owners.get(article) === token) owners.delete(article);
21
+ return true;
22
+ };
23
+ }
24
+
25
+ export function ownerOf(article) {
26
+ return owners.get(article)?.kind ?? null;
27
+ }
@@ -5,8 +5,10 @@
5
5
  // never run after this and undo it.
6
6
  import "../attrs/save-freeze.js";
7
7
  import { addDocumentTransform } from "../core/snapshot.js";
8
- import { HyperMorph } from "../vendor/hyper-morph.vendor.js";
8
+ import { parseJsonRelaxed } from "../vendor/hyper-morph-json.vendor.js";
9
9
  import { fragmentCleaner, project, renderModel, revOf } from "./projection.js";
10
+ import { documentTitle } from "./document-title.js";
11
+ import { customTitle, writeTitle } from "./document-title-dom.js";
10
12
  import { blocksFromPageValues } from "./ybind/decode.js";
11
13
  import { YDecodeError } from "./ybind/provider.js";
12
14
 
@@ -27,7 +29,7 @@ export function renderFromTags(texts) {
27
29
  const key = texts.join("\u0000");
28
30
  if (memo && memo.key === key) return memo;
29
31
  const values = texts.map((text) => {
30
- try { return HyperMorph.parseJsonRelaxed(text); }
32
+ try { return parseJsonRelaxed(text); }
31
33
  catch { throw new YDecodeError("invalid-json", "Cannot read the page data as JSON"); }
32
34
  });
33
35
  const blocks = blocksFromPageValues(values);
@@ -36,7 +38,7 @@ export function renderFromTags(texts) {
36
38
  const html = blocks[id]?.artifact?.fallbackHtml;
37
39
  return typeof html === "string" ? html : "";
38
40
  };
39
- memo = { key, html: renderModel(model, { fallbackOf, clean: fragmentCleaner(document) }), rev: revOf(model) };
41
+ memo = { key, html: renderModel(model, { fallbackOf, clean: fragmentCleaner(document) }), rev: revOf(model), model, title: documentTitle(model) };
40
42
  return memo;
41
43
  }
42
44
 
@@ -45,9 +47,49 @@ export function projectInto(clone) {
45
47
  if (!article) return;
46
48
  const tags = pageTags(clone);
47
49
  if (!tags.length) return;
48
- const { html, rev } = renderFromTags(tags.map((el) => el.textContent));
50
+ const { html, rev, title } = renderFromTags(tags.map((el) => el.textContent));
49
51
  if (article.innerHTML !== html) article.innerHTML = html;
50
52
  article.setAttribute("data-rev", rev);
53
+ if (!customTitle(clone)) writeTitle(clone, title);
54
+ }
55
+
56
+ /**
57
+ * The page state a final save clone carries, or null.
58
+ *
59
+ * One page tag is one page state: a Yjs page never has two, a tag holding plain block
60
+ * data is a page's own content rather than one, and a tag that does not parse is not one
61
+ * either. The value is copied, because the store keeps writing to its own after the
62
+ * capture and a checkpoint must prove the bytes this capture sent.
63
+ *
64
+ * Registered with core/snapshot only by the recovery integration, so a page that
65
+ * journals nothing never reads it.
66
+ *
67
+ * @param {HTMLElement} root - the save clone, after every transform
68
+ * @returns {?Object}
69
+ */
70
+ export function pageStateOfClone(root) {
71
+ const tags = pageTags(root);
72
+ if (tags.length !== 1) return null;
73
+ let value;
74
+ try { value = parseJsonRelaxed(tags[0].textContent); }
75
+ catch { return null; }
76
+ const { v, g, y } = value ?? {};
77
+ if (v !== 1 || !y || typeof y !== "object" || Array.isArray(y)) return null;
78
+ return { v, g, y: { ...y } };
79
+ }
80
+
81
+ /**
82
+ * The canonical article one page state projects to.
83
+ *
84
+ * The same projection the save writes into the capture's article, run over the state a
85
+ * capture pinned or a host handed back. A state whose projection is not the article
86
+ * that was accepted is not a proof of those bytes, whichever transform changed it.
87
+ *
88
+ * @param {Object|string} state - a page value, or the page tag text holding one
89
+ * @returns {string}
90
+ */
91
+ export function articleOfPageState(state) {
92
+ return renderFromTags([typeof state === "string" ? state : JSON.stringify(state)]).html;
51
93
  }
52
94
 
53
95
  addDocumentTransform(projectInto);
@@ -1,5 +1,5 @@
1
1
  import { captureForSaveAndComparison } from "../core/snapshot.js";
2
- import { downloadHtml, documentName } from "../core/conflict-download.js";
2
+ import { downloadHtml, documentName } from "../core/download.js";
3
3
 
4
4
  // One plain state per save refusal code, so the indicator, the toast and the
5
5
  // page plugin all read the same answer. `saved` is the only happy one; every
@@ -50,6 +50,12 @@ export const schema = new model.Schema({
50
50
  "data-checked": n.attrs.checked ? "true" : "false",
51
51
  }), 0],
52
52
  },
53
+ quote: {
54
+ group: "block",
55
+ content: "inline*",
56
+ attrs: block,
57
+ toDOM: (n) => ["blockquote", ref(n), 0],
58
+ },
53
59
  callout: {
54
60
  group: "block",
55
61
  content: "inline*",
@@ -141,6 +147,20 @@ export const schema = new model.Schema({
141
147
  attrs: { ...block, html: { default: "" } },
142
148
  toDOM: (n) => ["div", ref(n, { "data-type": "preserved" })],
143
149
  },
150
+ // A block type a newer peer wrote and this runtime has no schema for. It keeps
151
+ // the original record in raw and its inline content for mapping, and it shows
152
+ // an inert fallback: no content DOM to edit, no command that rewrites it.
153
+ opaque_block: {
154
+ group: "block",
155
+ content: "inline*",
156
+ atom: true,
157
+ isolating: true,
158
+ selectable: true,
159
+ attrs: { ...block, raw: { default: null } },
160
+ toDOM: (n) => ["div", ref(n, { "data-type": "newer-format", "data-format": String(n.attrs.raw?.t ?? ""), contenteditable: "false" }),
161
+ ["span", { class: "page-newer-format" }, `Newer format: ${String(n.attrs.raw?.t ?? "")}`],
162
+ ["span", { class: "page-newer-format-text" }, n.textContent]],
163
+ },
144
164
 
145
165
  text: { group: "inline" },
146
166
  hard_break: {
@@ -160,5 +180,32 @@ export const schema = new model.Schema({
160
180
  inclusive: false,
161
181
  toDOM: (m) => ["a", { href: safeHref(m.attrs.href) }, 0],
162
182
  },
183
+ // A person mention is ordinary text with this mark over it: the name is the label
184
+ // and `id` is the people registry's reference. It links nowhere and notifies nobody.
185
+ mention: {
186
+ attrs: { id: {} },
187
+ inclusive: false,
188
+ excludes: "mention date link",
189
+ toDOM: (m) => ["span", { "data-page-mention": m.attrs.id }, 0],
190
+ },
191
+ // A date mention is the calendar date as ordinary ISO text with this mark over it:
192
+ // `date` is YYYY-MM-DD, and no time, zone, reminder or relative word is stored.
193
+ date: {
194
+ attrs: { date: {} },
195
+ inclusive: false,
196
+ excludes: "mention date link",
197
+ toDOM: (m) => ["time", { datetime: m.attrs.date, "data-page-date": "" }, 0],
198
+ },
199
+ // The floor-1 formats (format-native.js): written as named descriptors,
200
+ // drawn as the elements the saved article keeps for them.
201
+ underline: { toDOM: () => ["u", 0] },
202
+ text_color: {
203
+ attrs: { color: {} },
204
+ toDOM: (m) => ["span", { "data-color": m.attrs.color }, 0],
205
+ },
206
+ highlight: {
207
+ attrs: { color: {} },
208
+ toDOM: (m) => ["mark", { "data-color": m.attrs.color }, 0],
209
+ },
163
210
  },
164
211
  });
@@ -0,0 +1,3 @@
1
+ import { state } from "../vendor/prosemirror.vendor.js";
2
+
3
+ export const rectKey = new state.PluginKey("tableRect");
@@ -11,10 +11,11 @@ import { clipboardSerializer } from "./blocks.js";
11
11
  import { asStep } from "./blocksel.js";
12
12
  import { announce } from "./announce.js";
13
13
  import { REMOTE_META } from "./ybind/layout.js";
14
+ import { rectKey } from "./table-rect-key.js";
14
15
 
15
- const { Plugin, PluginKey, TextSelection } = state;
16
+ const { Plugin, TextSelection } = state;
16
17
  const N = schema.nodes;
17
- export const rectKey = new PluginKey("tableRect");
18
+ export { rectKey };
18
19
 
19
20
  const findTable = (doc, id) => {
20
21
  let found = null;
@@ -171,7 +172,7 @@ export function tableSelectPlugin() {
171
172
  const step = { ArrowUp: [-1, 0], ArrowDown: [1, 0], ArrowLeft: [0, -1], ArrowRight: [0, 1] }[event.key];
172
173
  if (step) return extend(...step)(st, view.dispatch, view);
173
174
  }
174
- if (MODIFIERS.has(event.key) || ((event.metaKey || event.ctrlKey) && (event.key === "c" || event.key === "x" || event.key === "v"))) return false;
175
+ if (MODIFIERS.has(event.key) || ((event.metaKey || event.ctrlKey) && (event.key === "a" || event.key === "c" || event.key === "x" || event.key === "v"))) return false;
175
176
  view.dispatch(st.tr.setMeta(rectKey, null));
176
177
  return false;
177
178
  },
@@ -8,7 +8,7 @@ export const TABLE_CSS = `
8
8
  .page-table table.is-sized { table-layout: fixed; min-width: 0; }
9
9
  .page-table table.is-sized th, .page-table table.is-sized td { min-width: 0; overflow-wrap: anywhere; }
10
10
  .page-table-resize { position: absolute; top: 0; bottom: 0; inset-inline-end: -4px; width: 8px; z-index: 2; cursor: col-resize; touch-action: none; }
11
- .page-table-resize:hover, .page-table-resize:active { background: var(--page-accent); opacity: 0.4; }
11
+ .page-table-resize:hover, .page-table-resize:active, .page-table-resize:focus-visible { background: var(--page-accent); opacity: 0.4; }
12
12
  .page-table-add-row, .page-table-add-col { position: absolute; z-index: 1; border: 0; padding: 0; background: var(--page-chrome-bg-rest); color: var(--page-chrome-ink-rest); font: inherit; line-height: 1; cursor: pointer; opacity: 0; transition: opacity 120ms; }
13
13
  .page-table-add-row { left: 0; right: 0; top: 100%; height: 14px; border-radius: 0 0 6px 6px; }
14
14
  .page-table-add-col { top: 0; bottom: 0; left: 100%; width: 14px; border-radius: 0 6px 6px 0; }
@@ -0,0 +1,3 @@
1
+ // The cell views a table plugin can reach, filled by the table view part when it
2
+ // loads. The plugin works, and does nothing, for cells without a view.
3
+ export const cellViews = new WeakMap();