@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
@@ -10,12 +10,33 @@
10
10
  // pos-keyed maps. parse(render(blocks)) equals project(blocks) for every kind.
11
11
  import { chartModel, renderChartSvg } from "./rich/chart.js";
12
12
  import { cellText } from "./rich/cell.js";
13
+ import { bounded, descriptors, entriesOf, keyOf, normalizeEntries } from "./format-codec.js";
14
+ import { nativeAttrs } from "./format-native.js";
13
15
 
14
16
  export const ATOM = "";
15
17
  export const BREAK = '@{"type":"hard_break"}';
16
18
  const CODES = ["b", "i", "s", "c", "a"];
17
19
  const TAGS = { b: "strong", i: "em", s: "s", c: "code" };
18
20
  const NEST = ["a", "b", "i", "s", "c"];
21
+ const KNOWN = new Set(CODES);
22
+
23
+ // A block or an inline span the runtime has no schema for rides in an inert
24
+ // semantic envelope: canonical JSON, base64url, in one attribute. The block
25
+ // envelope holds the block record, the marks envelope the descriptors of one
26
+ // run, and the visible text stays the readable fallback.
27
+ export const OPAQUE = "data-clay-page-opaque";
28
+ export const VALUE = "data-clay-page-value";
29
+ export const MARKS = "data-clay-page-marks";
30
+ // A person mention's span states its id plainly beside the envelope that carries it:
31
+ // the label is the text, and a reader without the editor shows a name, never a link.
32
+ export const MENTION = "data-page-mention";
33
+ // A date mention reads as a `time` element: the text is the ISO date and `datetime`
34
+ // repeats it, so a reader without the editor shows the day and no zone or reminder.
35
+ export const DATE = "data-page-date";
36
+ const DAY = /^\d{4}-\d{2}-\d{2}$/;
37
+ const ENVELOPE = "1";
38
+ const STRUCTURAL = new Set(["id", "t", "parent", "c", "m"]);
39
+ const knownType = (t) => Object.hasOwn(DEFAULTS, t);
19
40
 
20
41
  // Mirrors schema.js attrs minus id and pos (a test pins the two together).
21
42
  export const DEFAULTS = {
@@ -23,6 +44,7 @@ export const DEFAULTS = {
23
44
  heading: { parent: null, level: 2 },
24
45
  list_item: { parent: null, kind: "bullet", indent: 0, checked: false, owner: null, due: null },
25
46
  callout: { parent: null },
47
+ quote: { parent: null },
26
48
  code_block: { parent: null, lang: "" },
27
49
  toggle: { parent: null, open: false },
28
50
  divider: { parent: null },
@@ -34,7 +56,7 @@ export const DEFAULTS = {
34
56
  tool: { parent: null, artifact: null, rev: null, state: {} },
35
57
  preserved: { parent: null, html: "" },
36
58
  };
37
- const TEXT = new Set(["paragraph", "heading", "list_item", "callout", "code_block", "toggle"]);
59
+ const TEXT = new Set(["paragraph", "heading", "list_item", "callout", "quote", "code_block", "toggle"]);
38
60
 
39
61
  const same = (a, b) => canon(a) === canon(b);
40
62
 
@@ -48,6 +70,67 @@ export function orderedIds(map) {
48
70
  });
49
71
  }
50
72
 
73
+ // ---- envelopes --------------------------------------------------------------
74
+
75
+ const b64url = (text) => {
76
+ let bin = "";
77
+ for (const byte of new TextEncoder().encode(text)) bin += String.fromCharCode(byte);
78
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
79
+ };
80
+
81
+ const fromB64url = (text) => {
82
+ const bin = atob(String(text).replace(/-/g, "+").replace(/_/g, "/"));
83
+ return new TextDecoder().decode(Uint8Array.from(bin, (ch) => ch.charCodeAt(0)));
84
+ };
85
+
86
+ // A key any object would take as its prototype rather than as data.
87
+ const hostile = (value) => {
88
+ if (value === null || typeof value !== "object") return false;
89
+ if (Array.isArray(value)) return value.some(hostile);
90
+ return Object.keys(value).some((key) => key === "__proto__" || hostile(value[key]));
91
+ };
92
+
93
+ // One envelope's JSON, under the codec's budget: deeper or larger refuses the
94
+ // whole source rather than cutting the envelope short.
95
+ function decodeJson(text) {
96
+ let value;
97
+ try { value = JSON.parse(fromB64url(text)); }
98
+ catch { throw new SyntaxError("page: an envelope is not readable canonical JSON"); }
99
+ try { bounded(value); }
100
+ catch (error) { throw new SyntaxError(error.message); }
101
+ if (hostile(value)) throw new SyntaxError("page: an envelope holds a prototype key");
102
+ return value;
103
+ }
104
+
105
+ // A block envelope: a record, whose own type and structural fields are kept
106
+ // apart from it.
107
+ function recordOf(text) {
108
+ const value = decodeJson(text);
109
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new SyntaxError("page: a block envelope is not a record");
110
+ if ("id" in value || "pos" in value) throw new SyntaxError("page: a block envelope holds a structural field");
111
+ if ("t" in value && (typeof value.t !== "string" || !value.t)) throw new SyntaxError("page: a block envelope names no type");
112
+ return value;
113
+ }
114
+
115
+ // A marks envelope: the descriptors of one run, validated and put back in the
116
+ // codec's canonical order however they arrived.
117
+ function entriesOfEnvelope(text) {
118
+ try { return normalizeEntries(decodeJson(text)); }
119
+ catch (error) { throw error instanceof SyntaxError ? error : new SyntaxError(error.message); }
120
+ }
121
+
122
+ // The entries of a marks envelope as it arrived, validated by the same reader
123
+ // the article uses.
124
+ export const marksEntries = (text) => entriesOfEnvelope(text);
125
+
126
+ const envelopeOf = (value) => ` ${OPAQUE}="${ENVELOPE}" ${VALUE}="${escAttr(b64url(canon(value)))}"`;
127
+ const marksEnvelope = (entries) => (entries.length ? ` ${MARKS}="${escAttr(b64url(JSON.stringify(entries)))}"` : "");
128
+
129
+ // The same two envelopes as attribute maps, for the internal rich clipboard,
130
+ // which writes them onto its own elements instead of an article line.
131
+ export const envelopeAttrs = (record) => ({ [OPAQUE]: ENVELOPE, [VALUE]: b64url(canon(record)) });
132
+ export const marksAttrs = (entries) => ({ [MARKS]: b64url(JSON.stringify(normalizeEntries(entries))) });
133
+
51
134
  // ---- marks ------------------------------------------------------------------
52
135
 
53
136
  // inline.js unitsOf: runs [[n, key]] or per-unit keys -> one key per unit, or null.
@@ -72,28 +155,36 @@ export function runsOf(units) {
72
155
  return out;
73
156
  }
74
157
 
75
- // A mark key as { b: true, a: ["href"] ... }; null for an atom key.
158
+ // A mark key as { marks: { b: true, a: ["href"] ... }, native: [...], entries: [...] }:
159
+ // the codes this runtime knows, the named descriptors of the four formats it
160
+ // writes (format-native.js), and the entries of every mark it has no code for,
161
+ // each in the codec's canonical order.
76
162
  function marksOfKey(key) {
77
- const out = {};
78
- if (!key || key[0] === "@") return out;
79
- for (let i = 0; i < key.length;) {
80
- const code = key[i++];
81
- if (key[i] === "[") {
82
- let depth = 0, str = false, j = i;
83
- for (; j < key.length; j++) {
84
- const ch = key[j];
85
- if (str) { if (ch === "\\") j++; else if (ch === '"') str = false; }
86
- else if (ch === '"') str = true;
87
- else if (ch === "[") depth++;
88
- else if (ch === "]" && --depth === 0) { j++; break; }
89
- }
90
- out[code] = JSON.parse(key.slice(i, j));
91
- i = j;
92
- } else out[code] = true;
163
+ const marks = {}, native = [];
164
+ if (!key || key[0] === "@") return { marks, native: [], entries: [] };
165
+ const opaque = [];
166
+ for (const desc of descriptors(key)) {
167
+ if (desc.name === undefined) {
168
+ if (KNOWN.has(desc.code)) marks[desc.code] = desc.attrs ?? true;
169
+ else opaque.push(desc);
170
+ continue;
171
+ }
172
+ const attrs = nativeAttrs(desc.name, desc.attrs);
173
+ if (attrs) native.push({ name: desc.name, attrs });
174
+ else opaque.push(desc);
93
175
  }
94
- return out;
176
+ return { marks, native, entries: opaque.length ? entriesOf(opaque) : [] };
95
177
  }
96
178
 
179
+ // One unit's key, as the record keeps it: the codes this runtime knows, then
180
+ // every descriptor it keeps for a newer runtime.
181
+ const unitKey = (key, text, i) => {
182
+ if (!key) return "";
183
+ if (key[0] === "@") return key === BREAK && text[i] === ATOM ? key : "";
184
+ const { marks, native, entries } = marksOfKey(key);
185
+ return keyOfMarks(safeMarks(marks), native.length ? [...entries, ...entriesOf(native)] : entries);
186
+ };
187
+
97
188
  // A link to a script URL is never kept: it would run on the page's origin when a
98
189
  // reader clicks it in the saved article.
99
190
  const SCRIPT_URL = /^(javascript|vbscript|data):/i;
@@ -102,14 +193,14 @@ const safeMarks = (marks) => {
102
193
  return marks;
103
194
  };
104
195
 
105
- function keyOfMarks(marks) {
196
+ function keyOfMarks(marks, entries = []) {
106
197
  let key = "";
107
198
  for (const code of CODES) {
108
199
  if (!marks[code]) continue;
109
200
  key += code;
110
201
  if (Array.isArray(marks[code])) key += JSON.stringify(marks[code]);
111
202
  }
112
- return key;
203
+ return entries.length ? key + keyOf(entries) : key;
113
204
  }
114
205
 
115
206
  // ---- text normalization -----------------------------------------------------
@@ -175,7 +266,7 @@ function layout(blocks) {
175
266
  let prev = null;
176
267
  for (const id of orderedIds(blocks)) {
177
268
  const b = blocks[id];
178
- const t = DEFAULTS[b.t] ? b.t : null;
269
+ const t = typeof b.t === "string" && b.t ? b.t : null;
179
270
  let parent = b.parent ?? null;
180
271
  if (parent !== null) {
181
272
  const at = stack.lastIndexOf(parent);
@@ -207,7 +298,7 @@ const columnWidth = (value) => (typeof value === "number" && Number.isFinite(val
207
298
  function cellModel(value) {
208
299
  if (value === null || typeof value !== "object" || typeof value.c !== "string") return normalizeText(cellText(value), null).c;
209
300
  const raw = unitsOf(value.m, value.c.length);
210
- const units = raw && raw.map((k, i) => (!k ? "" : k[0] === "@" ? (k === BREAK && value.c[i] === ATOM ? k : "") : keyOfMarks(safeMarks(marksOfKey(k)))));
301
+ const units = raw && raw.map((k, i) => unitKey(k, value.c, i));
211
302
  if (!units) return normalizeText(cellText(value), null).c;
212
303
  const n = normalizeText(value.c, units);
213
304
  const fields = textFields(n.c, n.units);
@@ -240,10 +331,44 @@ function projectBoard(b) {
240
331
  };
241
332
  }
242
333
 
334
+ // The record a block envelope carries: its own type first, then every field the
335
+ // runtime keeps for the block, extras included.
336
+ const recordOfItem = (item) => {
337
+ const { t, parent: _p, id: _i, ...fields } = item;
338
+ return { t, ...fields };
339
+ };
340
+
341
+ // The fields of a known block the runtime has no schema attribute for. They are
342
+ // merged back only when serialized, inside the inert payload.
343
+ const extrasOfItem = (item) => {
344
+ const out = {};
345
+ for (const [k, v] of Object.entries(item)) if (!STRUCTURAL.has(k) && !(knownType(item.t) && Object.hasOwn(DEFAULTS[item.t], k))) out[k] = v;
346
+ return Object.keys(out).length ? envelopeOf(out) : "";
347
+ };
348
+
349
+ const extrasOfBlock = (t, b) => {
350
+ const out = {};
351
+ for (const [k, v] of Object.entries(b)) if (!STRUCTURAL.has(k) && k !== "pos" && !Object.hasOwn(DEFAULTS[t], k)) out[k] = v;
352
+ return out;
353
+ };
354
+
243
355
  function projectOne({ id, b, t, parent, indent }) {
244
356
  if (!t) return { id, t: "preserved", html: "" };
245
- const { t: _t, pos, c, m, rows, cols, columns, cards, parent: _p, indent: _i, ...attrs } = b;
246
- for (const k of Object.keys(attrs)) if (!(k in DEFAULTS[t])) delete attrs[k];
357
+ if (!knownType(t)) {
358
+ const { t: _t, id: _id, pos: _pos, parent: _p, ...rest } = b;
359
+ // The text and its marks are kept as they stand, but marks in the older
360
+ // per-unit form come back as runs, so one record has one revision whichever
361
+ // form it was written in.
362
+ if (typeof rest.c === "string" && rest.m !== undefined) {
363
+ const units = unitsOf(rest.m, rest.c.length);
364
+ if (units && units.some(Boolean)) rest.m = runsOf(units);
365
+ else delete rest.m;
366
+ }
367
+ return { id, t, ...(parent ? { parent } : {}), ...rest };
368
+ }
369
+ const { t: _t, id: _id, pos, c, m, rows, cols, columns, cards, parent: _p, indent: _i, ...attrs } = b;
370
+ const extras = extrasOfBlock(t, attrs);
371
+ for (const k of Object.keys(extras)) delete attrs[k];
247
372
  const fields = { ...attrs, parent };
248
373
  if (t === "list_item") fields.indent = indent;
249
374
  if (t === "heading") fields.level = clampLevel(attrs.level ?? 2);
@@ -253,13 +378,13 @@ function projectOne({ id, b, t, parent, indent }) {
253
378
  if (t === "chart" && typeof fields.title === "string") fields.title = normalizeText(fields.title, null).c;
254
379
  if (t === "table") Object.assign(fields, projectTable(b));
255
380
  else if (t === "board") Object.assign(fields, projectBoard(b));
256
- const item = { id, t, ...omitDefaults(t, fields) };
381
+ const item = { id, t, ...omitDefaults(t, fields), ...extras };
257
382
  if (t === "table") { item.cols = fields.cols; item.rows = fields.rows; }
258
383
  if (t === "board") { item.columns = fields.columns; item.cards = fields.cards; }
259
384
  if (TEXT.has(t)) {
260
385
  const text = typeof c === "string" ? c : "";
261
386
  const raw = unitsOf(m, text.length);
262
- const units = raw && raw.map((k, i) => (!k ? "" : k[0] === "@" ? (text[i] === ATOM ? k : "") : keyOfMarks(safeMarks(marksOfKey(k)))));
387
+ const units = raw && raw.map((k, i) => unitKey(k, text, i));
263
388
  if (t === "code_block") Object.assign(item, { c: text.replace(/\r\n?/g, "\n") });
264
389
  else {
265
390
  const n = normalizeText(text, units);
@@ -301,7 +426,11 @@ const escText = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>
301
426
  const escAttr = (s) => escText(String(s)).replace(/"/g, "&quot;");
302
427
  const attr = (name, value) => (value === null || value === undefined ? "" : ` ${name}="${escAttr(value)}"`);
303
428
 
304
- export function renderInline(c, m) {
429
+ // The element each of the four formats is read as in the saved article; the
430
+ // text color and the highlight carry their palette key as data-color.
431
+ const NATIVE_TAGS = { underline: "u", text_color: "span", highlight: "mark" };
432
+
433
+ export function renderInline(c, m, marksEnvelopes = true) {
305
434
  const units = unitsOf(m, c.length) || [];
306
435
  let html = "";
307
436
  const open = [];
@@ -316,12 +445,22 @@ export function renderInline(c, m) {
316
445
  }
317
446
  let j = i + 1;
318
447
  while (j < c.length && (units[j] || "") === key && !(c[j] === ATOM && key[0] === "@")) j++;
319
- const marks = marksOfKey(key);
448
+ const { marks, native, entries } = marksOfKey(key);
320
449
  const want = NEST.filter((code) => marks[code]).map((code) => ({ code, sig: code + JSON.stringify(marks[code]), tag: code === "a" ? "a" : TAGS[code], href: code === "a" ? marks.a[0] : null }));
450
+ for (const n of native) want.push({ sig: `native${n.name}${canon(n.attrs)}`, tag: NATIVE_TAGS[n.name], color: n.attrs.color ?? null });
451
+ const mention = entries.find((entry) => entry.name === "mention");
452
+ const date = entries.find((entry) => entry.name === "date");
453
+ const day = date && DAY.test(String(date.attrs.date)) ? String(date.attrs.date) : null;
454
+ if (entries.length && marksEnvelopes) want.push({ sig: `marks${canon(entries)}`, tag: day ? "time" : "span", entries, mention: mention?.attrs.id, day });
321
455
  let keep = 0;
322
456
  while (keep < open.length && keep < want.length && open[keep].sig === want[keep].sig) keep++;
323
457
  closeTo(keep);
324
- for (const w of want.slice(keep)) { html += w.tag === "a" ? `<a${attr("href", w.href)}>` : `<${w.tag}>`; open.push(w); }
458
+ for (const w of want.slice(keep)) {
459
+ if (w.entries) html += w.day ? `<time${attr("datetime", w.day)}${attr(DATE, "")}${marksEnvelope(w.entries)}>` : `<span${marksEnvelope(w.entries)}${attr(MENTION, w.mention)}>`;
460
+ else if (w.tag === "a") html += `<a${attr("href", w.href)}>`;
461
+ else html += w.color ? `<${w.tag}${attr("data-color", w.color)}>` : `<${w.tag}>`;
462
+ open.push(w);
463
+ }
325
464
  html += escText(c.slice(i, j));
326
465
  i = j;
327
466
  }
@@ -338,26 +477,29 @@ const dataAttrs = (obj) => Object.entries(obj).map(([k, v]) => attr(`data-${k}`,
338
477
 
339
478
  function renderLeaf(item, clean, tables) {
340
479
  const id = attr("data-id", item.id);
480
+ if (!knownType(item.t)) return `<div${id}${envelopeOf(recordOfItem(item))}>${renderInline(item.c ?? "", item.m, false)}</div>`;
481
+ const at = id + extrasOfItem(item);
341
482
  switch (item.t) {
342
- case "paragraph": return `<p${id}>${inlineOf(item)}</p>`;
343
- case "heading": { const h = `h${item.level ?? 2}`; return `<${h}${id}>${inlineOf(item)}</${h}>`; }
344
- case "callout": return `<aside${id} class="page-callout">${inlineOf(item)}</aside>`;
345
- case "code_block": return `<pre${id}${attr("data-lang", item.lang || null)}><code>${escText(item.c ?? "")}</code></pre>`;
346
- case "divider": return `<hr${id}>`;
347
- case "image": return `<figure${id} data-type="image"${attr("data-align", item.align ?? null)}><img${attr("src", item.src ?? "")}${attr("alt", item.alt ?? "")}${attr("width", item.width ?? null)}>${item.caption ? `<figcaption>${escText(item.caption)}</figcaption>` : ""}</figure>`;
483
+ case "paragraph": return `<p${at}>${inlineOf(item)}</p>`;
484
+ case "heading": { const h = `h${item.level ?? 2}`; return `<${h}${at}>${inlineOf(item)}</${h}>`; }
485
+ case "callout": return `<aside${at} class="page-callout">${inlineOf(item)}</aside>`;
486
+ case "quote": return `<blockquote${at}>${inlineOf(item)}</blockquote>`;
487
+ case "code_block": return `<pre${at}${attr("data-lang", item.lang || null)}><code>${escText(item.c ?? "")}</code></pre>`;
488
+ case "divider": return `<hr${at}>`;
489
+ case "image": return `<figure${at} data-type="image"${attr("data-align", item.align ?? null)}><img${attr("src", item.src ?? "")}${attr("alt", item.alt ?? "")}${attr("width", item.width ?? null)}>${item.caption ? `<figcaption>${escText(item.caption)}</figcaption>` : ""}</figure>`;
348
490
  case "file": {
349
491
  const href = /^(https?:\/\/|\/(?!\/))/i.test(item.src ?? "") ? item.src : null;
350
- return `<div${id} data-type="file"${attr("data-src", item.src ?? "")}${attr("data-name", item.name ?? "")}${attr("data-size", item.size ?? null)}${attr("data-mime", item.mime ?? null)}><a${attr("href", href)} rel="noopener noreferrer">${escText(item.name || "file")}</a></div>`;
492
+ return `<div${at} data-type="file"${attr("data-src", item.src ?? "")}${attr("data-name", item.name ?? "")}${attr("data-size", item.size ?? null)}${attr("data-mime", item.mime ?? null)}><a${attr("href", href)} rel="noopener noreferrer">${escText(item.name || "file")}</a></div>`;
351
493
  }
352
494
  case "table": {
353
495
  const head = item.cols.map((c) => `<th${attr("data-col", c.id)}${attr("data-kind", c.kind ?? null)}${attr("data-width", c.width ?? null)}${attr("style", c.width ? `width:${c.width}px` : null)}>${escText(c.name)}</th>`).join("");
354
496
  const body = item.rows.map((r) => `<tr${attr("data-id", r.id)}>${item.cols.map((c) => `<td>${cellHtml(r.cells[c.id])}</td>`).join("")}</tr>`).join("\n");
355
- return `<table${id}${attr("data-sort", item.sort != null ? JSON.stringify(item.sort) : null)}>\n<thead><tr>${head}</tr></thead>\n<tbody>\n${body}\n</tbody>\n</table>`;
497
+ return `<table${at}${attr("data-sort", item.sort != null ? JSON.stringify(item.sort) : null)}>\n<thead><tr>${head}</tr></thead>\n<tbody>\n${body}\n</tbody>\n</table>`;
356
498
  }
357
499
  case "chart": {
358
500
  const data = dataAttrs({ source: item.source ?? null, kind: item.kind ?? null, x: item.x ?? null, series: item.series ? JSON.stringify(item.series) : null, highlight: item.highlight ?? null });
359
501
  const chart = chartModel({ id: item.id, type: item.kind ?? "line", x: item.x, series: item.series ?? [], title: item.title ?? "", highlight: item.highlight }, tables.get(item.source));
360
- return `<figure${id} data-type="chart"${data}>${renderChartSvg(chart, { width: 640, height: 320 })}<figcaption>${escText(item.title ?? "")}</figcaption></figure>`;
502
+ return `<figure${at} data-type="chart"${data}>${renderChartSvg(chart, { width: 640, height: 320 })}<figcaption>${escText(item.title ?? "")}</figcaption></figure>`;
361
503
  }
362
504
  case "board": {
363
505
  const cols = [...item.columns.map((c) => ({ ...c, known: true })), ...[...new Set(item.cards.map((k) => k.col))].filter((col) => !item.columns.some((c) => c.id === col)).map((col) => ({ id: col, known: false }))];
@@ -365,16 +507,16 @@ function renderLeaf(item, clean, tables) {
365
507
  const cards = item.cards.filter((k) => k.col === col.id).map((k) => `<li${attr("data-id", k.id)}${attr("data-owner", k.owner ?? null)}${attr("data-due", k.due ?? null)}>${escText(k.title)}</li>`).join("");
366
508
  return `<div${attr("data-col", col.id)}${col.known ? "" : " data-missing"}>${col.known ? `<h4>${escText(col.name)}</h4>` : ""}<ul>${cards}</ul></div>`;
367
509
  });
368
- return `<section${id} data-type="board">\n${parts.join("\n")}\n</section>`;
510
+ return `<section${at} data-type="board">\n${parts.join("\n")}\n</section>`;
369
511
  }
370
- case "tool": return `<section${id} data-type="tool">${toolData(item)}</section>`;
371
- case "preserved": return preservedHtml(item, clean);
512
+ case "tool": return `<section${at} data-type="tool">${toolData(item)}</section>`;
513
+ case "preserved": return preservedHtml(item, clean, at);
372
514
  default: return "";
373
515
  }
374
516
  }
375
517
 
376
518
  // Markup the schema has no node for, kept as authored inside a marked wrapper.
377
- const preservedHtml = (item, clean) => `<div${attr("data-id", item.id)} data-type="preserved">${clean.preserved(item.html || "")}</div>`;
519
+ const preservedHtml = (item, clean, id = attr("data-id", item.id)) => `<div${id} data-type="preserved">${clean.preserved(item.html || "")}</div>`;
378
520
 
379
521
  const RAW = { preserved: (html) => html, fallback: (html) => html };
380
522
 
@@ -423,11 +565,11 @@ export function renderModel(model, { fallbackOf = () => "", clean = RAW } = {})
423
565
  }
424
566
  if (lists.length < d + 1) { add(`<${tag}>`); lists.push(tag); }
425
567
  const box = item.kind === "check" ? `<input type="checkbox" disabled${item.checked ? " checked" : ""}>` : "";
426
- add(`<li${attr("data-id", item.id)}${attr("data-owner", item.owner ?? null)}${attr("data-due", item.due ?? null)}>${box}${inlineOf(item)}`);
568
+ add(`<li${attr("data-id", item.id)}${attr("data-owner", item.owner ?? null)}${attr("data-due", item.due ?? null)}${extrasOfItem(item)}>${box}${inlineOf(item)}`);
427
569
  continue;
428
570
  }
429
571
  if (item.t === "toggle") {
430
- add(`<details${attr("data-id", item.id)}${item.open ? " open" : ""}><summary>${inlineOf(item)}</summary>`);
572
+ add(`<details${attr("data-id", item.id)}${extrasOfItem(item)}${item.open ? " open" : ""}><summary>${inlineOf(item)}</summary>`);
431
573
  stack.push(item.id);
432
574
  continue;
433
575
  }
@@ -449,16 +591,27 @@ export function renderBlocks(blocks, opts) {
449
591
  const DROP_TAGS = new Set(["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"]);
450
592
  const INLINE_TAGS = new Set(["A", "ABBR", "B", "BDI", "BDO", "BR", "CITE", "CODE", "DATA", "DEL", "DFN", "EM", "FONT", "I", "INS", "KBD", "LABEL", "MARK", "Q", "S", "SAMP", "SMALL", "SPAN", "STRIKE", "STRONG", "SUB", "SUP", "TIME", "U", "VAR", "WBR"]);
451
593
 
594
+ // The named descriptor an inline element stands for, when it is one of the four
595
+ // formats: <u>, <mark> and a <span data-color> with a palette key. Anything else
596
+ // is not a format this runtime reads natively.
597
+ function nativeOfEl(tag, el) {
598
+ if (tag === "u") return { name: "underline", attrs: {} };
599
+ if (tag !== "mark" && tag !== "span") return null;
600
+ if (tag === "span" && !el.hasAttribute("data-color")) return null;
601
+ const name = tag === "mark" ? "highlight" : "text_color";
602
+ return { name, attrs: { color: el.getAttribute("data-color") } };
603
+ }
604
+
452
605
  // Inline content of el as { c, units }, minus the children skip(child) names.
453
606
  function inlineOfEl(el, skip = () => false) {
454
607
  let c = "";
455
608
  const units = [];
456
- const walk = (node, marks) => {
609
+ const walk = (node, marks, entries) => {
457
610
  for (const child of node.childNodes) {
458
611
  if (skip(child)) continue;
459
612
  if (child.nodeType === 3) {
460
613
  c += child.data;
461
- const key = keyOfMarks(safeMarks({ ...marks }));
614
+ const key = keyOfMarks(safeMarks({ ...marks }), entries);
462
615
  for (let i = 0; i < child.data.length; i++) units.push(key);
463
616
  continue;
464
617
  }
@@ -467,20 +620,45 @@ function inlineOfEl(el, skip = () => false) {
467
620
  if (tag === "br") { c += ATOM; units.push(BREAK); continue; }
468
621
  if (tag === "span" && child.hasAttribute("data-atom")) { c += ATOM; units.push(child.getAttribute("data-atom")); continue; }
469
622
  const next = { ...marks };
623
+ if ((tag === "span" || tag === "time") && child.hasAttribute(MARKS)) {
624
+ walk(child, next, normalizeEntries([...entries, ...entriesOfEnvelope(child.getAttribute(MARKS))]));
625
+ continue;
626
+ }
470
627
  if (tag === "strong" || tag === "b") next.b = true;
471
628
  else if (tag === "em" || tag === "i") next.i = true;
472
629
  else if (tag === "s" || tag === "del" || tag === "strike") next.s = true;
473
630
  else if (tag === "code") next.c = true;
474
631
  else if (tag === "a" && child.hasAttribute("href")) next.a = [child.getAttribute("href")];
475
- walk(child, next);
632
+ const named = nativeOfEl(tag, child);
633
+ walk(child, next, named ? [...entries, named] : entries);
476
634
  }
477
635
  };
478
- walk(el, {});
636
+ walk(el, {}, []);
479
637
  return normalizeText(c, units);
480
638
  }
481
639
 
482
640
  const text = (el) => normalizeText(el ? el.textContent : "", null).c;
483
641
 
642
+ // The block envelope an element carries, or null. A malformed, oversized or
643
+ // hostile one refuses the whole article: it never falls back to plain text.
644
+ const envelopeAt = (el) => {
645
+ if (!el.hasAttribute(OPAQUE)) return null;
646
+ if (el.getAttribute(OPAQUE) !== ENVELOPE) throw new SyntaxError(`page: unknown envelope version ${el.getAttribute(OPAQUE)}`);
647
+ const text = el.getAttribute(VALUE);
648
+ if (text === null) throw new SyntaxError("page: a block envelope holds no value");
649
+ return recordOf(text);
650
+ };
651
+
652
+ // The record a block envelope carries, validated; null when the element carries
653
+ // no envelope at all.
654
+ export const envelopeRecord = (el) => envelopeAt(el);
655
+
656
+ // The extras a known element's envelope adds to it.
657
+ const extrasAt = (el) => {
658
+ const value = envelopeAt(el);
659
+ return value && typeof value.t !== "string" ? value : null;
660
+ };
661
+
484
662
  export function parseArticle(article) {
485
663
  const items = [];
486
664
  const walk = (container, parent) => {
@@ -506,7 +684,10 @@ export function parseArticle(article) {
506
684
  flushLoose();
507
685
  };
508
686
  const idOf = (el) => el.getAttribute("data-id") || null;
509
- const push = (el, t, parent, fields) => items.push({ id: idOf(el), t, parent, ...fields });
687
+ const push = (el, t, parent, fields) => {
688
+ const extra = extrasAt(el);
689
+ items.push({ id: idOf(el), t, parent, ...(extra ? { ...extra, ...fields } : fields) });
690
+ };
510
691
  const list = (el, parent, depth) => {
511
692
  for (const li of el.children) {
512
693
  if (li.tagName !== "LI") continue;
@@ -515,17 +696,25 @@ export function parseArticle(article) {
515
696
  const n = inlineOfEl(li, (child) => child === box || nested.includes(child));
516
697
  const kind = box ? "check" : el.tagName === "OL" ? "ordered" : "bullet";
517
698
  const fields = { parent, kind, indent: depth, checked: !!(box && box.hasAttribute("checked")), owner: li.getAttribute("data-owner"), due: li.getAttribute("data-due") };
518
- items.push({ id: idOf(li), t: "list_item", ...omitDefaults("list_item", fields), ...textFields(n.c, n.units) });
699
+ items.push({ id: idOf(li), t: "list_item", ...omitDefaults("list_item", fields), ...(extrasAt(li) ?? {}), ...textFields(n.c, n.units) });
519
700
  for (const sub of nested) list(sub, parent, depth + 1);
520
701
  }
521
702
  };
522
703
  const blockOf = (el, parent) => {
523
704
  const tag = el.tagName;
524
705
  const P = { parent };
706
+ const record = envelopeAt(el);
707
+ if (record && typeof record.t === "string") {
708
+ if (knownType(record.t) || record.t === "opaque_block") throw new SyntaxError(`page: an envelope names the known block type ${record.t}`);
709
+ const { t, ...fields } = record;
710
+ items.push({ id: idOf(el), t, parent, ...fields });
711
+ return;
712
+ }
525
713
  const onlyImage = tag === "P" && el.children.length === 1 && el.children[0].tagName === "IMG" && !el.textContent.trim();
526
714
  if (tag === "P" && !onlyImage) return push(el, "paragraph", parent, { ...omitDefaults("paragraph", P), ...tf(el) });
527
715
  if (/^H[1-6]$/.test(tag)) return push(el, "heading", parent, { ...omitDefaults("heading", { ...P, level: +tag[1] }), ...tf(el) });
528
716
  if (tag === "ASIDE") return push(el, "callout", parent, { ...omitDefaults("callout", P), ...tf(el) });
717
+ if (tag === "BLOCKQUOTE") return push(el, "quote", parent, { ...omitDefaults("quote", P), ...tf(el) });
529
718
  if (tag === "PRE") {
530
719
  const code = el.querySelector(":scope > code");
531
720
  return push(el, "code_block", parent, { ...omitDefaults("code_block", { ...P, lang: el.getAttribute("data-lang") || "" }), c: (code || el).textContent });