@panphora/clayjs 1.15.2 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/README.md +4 -5
  2. package/THIRD-PARTY-NOTICES.md +16 -0
  3. package/compat/highlight-11.9.0.min.js +1213 -0
  4. package/compat/highlight-LICENSE +29 -0
  5. package/compat/journal-assets.lock.json +22 -0
  6. package/compat/overtype-2.6.1.min.js +116 -0
  7. package/compat/overtype-LICENSE +21 -0
  8. package/dist/all.js +90 -0
  9. package/dist/clay-dom.js +84 -0
  10. package/dist/clay-events.js +103 -0
  11. package/dist/clay-internals.js +85 -0
  12. package/dist/clay-options.js +93 -0
  13. package/dist/clay-router.js +98 -0
  14. package/dist/clay-ui.js +95 -0
  15. package/dist/clay-utils.js +84 -0
  16. package/dist/clay.js +146 -0
  17. package/dist/clay.standalone.js +83090 -58706
  18. package/dist/m/attrs/add-from-T5LJXFFI.js +1 -0
  19. package/dist/m/attrs/onaftersave-OKSWEJQF.js +1 -0
  20. package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +1 -0
  21. package/dist/m/attrs/save-freeze-FUP4Q4X3.js +1 -0
  22. package/dist/m/c-2AZDZWCK.js +1 -0
  23. package/dist/m/c-2JL6E75N.js +1 -0
  24. package/dist/m/c-2M73252O.js +1 -0
  25. package/dist/m/c-2SPVQSHC.js +1 -0
  26. package/dist/m/c-33HUYEDI.js +1 -0
  27. package/dist/m/c-33JJ6CBX.js +2 -0
  28. package/dist/m/c-35NBGMZB.js +1 -0
  29. package/dist/m/c-3GJ4SDE2.js +1 -0
  30. package/dist/m/c-3IQRECYS.js +1 -0
  31. package/dist/m/c-3OWDMNJR.js +2 -0
  32. package/dist/m/c-46XYM55L.js +1 -0
  33. package/dist/m/c-4B6H4G5V.js +1 -0
  34. package/dist/m/c-4DRMS4QG.js +9 -0
  35. package/dist/m/c-4FFHZM32.js +1 -0
  36. package/dist/m/c-4JZDZFF6.js +1 -0
  37. package/dist/m/c-4L3BTFRK.js +1 -0
  38. package/dist/m/c-4Q2XTQ2N.js +2 -0
  39. package/dist/m/c-4WKFKEKF.js +1 -0
  40. package/dist/m/c-4YSCRICO.js +1 -0
  41. package/dist/m/c-54RCDCBU.js +1 -0
  42. package/dist/m/c-5M4YEKCX.js +4 -0
  43. package/dist/m/c-5R4PTRG2.js +1 -0
  44. package/dist/m/c-5V3MOVJT.js +1 -0
  45. package/dist/m/c-5VYOWDTI.js +1 -0
  46. package/dist/m/c-5Y6TNX5W.js +34 -0
  47. package/dist/m/c-5YIHJ3TJ.js +1 -0
  48. package/dist/m/c-66O32MV3.js +28 -0
  49. package/dist/m/c-6HMB24EV.js +1 -0
  50. package/dist/m/c-7DJDKPGL.js +1 -0
  51. package/dist/m/c-7O6AFR65.js +1 -0
  52. package/dist/m/c-7SWMYKAI.js +1 -0
  53. package/dist/m/c-7VGZ7RU5.js +1 -0
  54. package/dist/m/c-A7LS5UZW.js +1 -0
  55. package/dist/m/c-ANQTJ3B5.js +2 -0
  56. package/dist/m/c-AQLFEATB.js +1 -0
  57. package/dist/m/c-AYVOREBE.js +1 -0
  58. package/dist/m/c-B2ZXPOS3.js +3 -0
  59. package/dist/m/c-BBVGTQG7.js +1 -0
  60. package/dist/m/c-BMIZKMPV.js +1 -0
  61. package/dist/m/c-BTDI4Y23.js +1 -0
  62. package/dist/m/c-BWFVSBDF.js +1 -0
  63. package/dist/m/c-BYTKL6UX.js +1 -0
  64. package/dist/m/c-CFHW5AMY.js +6 -0
  65. package/dist/m/c-CJZSLMVQ.js +1 -0
  66. package/dist/m/c-CYWLPPUS.js +1 -0
  67. package/dist/m/c-CZUSOB7V.js +1 -0
  68. package/dist/m/c-DBE6ORSI.js +1 -0
  69. package/dist/m/c-DCFSEJZG.js +1 -0
  70. package/dist/m/c-DNZ5IFM6.js +1 -0
  71. package/dist/m/c-EDHUR4MX.js +1 -0
  72. package/dist/m/c-EJTGYSLN.js +1 -0
  73. package/dist/m/c-EY6UOM54.js +1 -0
  74. package/dist/m/c-F3CKJTJS.js +1 -0
  75. package/dist/m/c-F7Y6FLPY.js +1 -0
  76. package/dist/m/c-FA4DZVTC.js +1 -0
  77. package/dist/m/c-FILLTJA7.js +1 -0
  78. package/dist/m/c-G2BUZITR.js +1 -0
  79. package/dist/m/c-GB3JTV6E.js +1 -0
  80. package/dist/m/c-GBGWXTX4.js +1 -0
  81. package/dist/m/c-GHBCCVG4.js +1 -0
  82. package/dist/m/c-GJAEGGXY.js +1 -0
  83. package/dist/m/c-GOWBDM6J.js +1 -0
  84. package/dist/m/c-GQ6F6IDK.js +1 -0
  85. package/dist/m/c-H4LG442Q.js +1 -0
  86. package/dist/m/c-HA6INT6J.js +1 -0
  87. package/dist/m/c-HPURYVZS.js +2 -0
  88. package/dist/m/c-HRDCD3WN.js +67 -0
  89. package/dist/m/c-HT33EQRN.js +1 -0
  90. package/dist/m/c-HVYXIS72.js +1 -0
  91. package/dist/m/c-IB52R237.js +1 -0
  92. package/dist/m/c-IJUK2GPW.js +1 -0
  93. package/dist/m/c-IKWNE4R4.js +1 -0
  94. package/dist/m/c-IU3IYEU6.js +1 -0
  95. package/dist/m/c-IZ2EBRAJ.js +1 -0
  96. package/dist/m/c-J7CA54IP.js +1 -0
  97. package/dist/m/c-JAX2667S.js +6 -0
  98. package/dist/m/c-KI7M4AY3.js +1 -0
  99. package/dist/m/c-KIPQJX4B.js +1 -0
  100. package/dist/m/c-KKWLDOPM.js +1 -0
  101. package/dist/m/c-KR4UW4KW.js +1 -0
  102. package/dist/m/c-KY3VJGUA.js +8 -0
  103. package/dist/m/c-L4WQSC27.js +1 -0
  104. package/dist/m/c-LFPHCXY3.js +1 -0
  105. package/dist/m/c-LR4CGEPT.js +91 -0
  106. package/dist/m/c-LRPUO2MX.js +1 -0
  107. package/dist/m/c-LTGMXXKO.js +1 -0
  108. package/dist/m/c-LXENEBV7.js +1 -0
  109. package/dist/m/c-LZJPNR2W.js +1 -0
  110. package/dist/m/c-MMUIUUZG.js +2 -0
  111. package/dist/m/c-MTQHWQC3.js +1 -0
  112. package/dist/m/c-N326R2JD.js +1 -0
  113. package/dist/m/c-N7JPUPGC.js +1 -0
  114. package/dist/m/c-NMJRXKPC.js +8 -0
  115. package/dist/m/c-NRPJEVJS.js +1 -0
  116. package/dist/m/c-NUHP2XDB.js +1 -0
  117. package/dist/m/c-NZMNXVF6.js +1 -0
  118. package/dist/m/c-OCSZ4QGN.js +1 -0
  119. package/dist/m/c-OIBUTDXI.js +1 -0
  120. package/dist/m/c-OIDRZKAF.js +1 -0
  121. package/dist/m/c-OYTFP5QN.js +1 -0
  122. package/dist/m/c-P7KNDG34.js +1 -0
  123. package/dist/m/c-PPOSCAQD.js +1 -0
  124. package/dist/m/c-PQ5LK6QE.js +993 -0
  125. package/dist/m/c-PZ4XEPUP.js +2 -0
  126. package/dist/m/c-QHIWXHRO.js +1 -0
  127. package/dist/m/c-QHWJ5ZIJ.js +1 -0
  128. package/dist/m/c-QIX5BJYN.js +1 -0
  129. package/dist/m/c-QNKREOL6.js +1 -0
  130. package/dist/m/c-QQHEX6HA.js +1 -0
  131. package/dist/m/c-R7RMANJI.js +9 -0
  132. package/dist/m/c-RI4NT75Y.js +1 -0
  133. package/dist/m/c-RM7J7BV2.js +1 -0
  134. package/dist/m/c-RUJKSUQW.js +1 -0
  135. package/dist/m/c-RVMWGXEJ.js +1 -0
  136. package/dist/m/c-S7H2DRCB.js +1 -0
  137. package/dist/m/c-S7LK4KUE.js +1 -0
  138. package/dist/m/c-SGCCC4FW.js +25 -0
  139. package/dist/m/c-SGIA4IEQ.js +188 -0
  140. package/dist/m/c-SWRJFCNN.js +1 -0
  141. package/dist/m/c-T4VO4OCB.js +1 -0
  142. package/dist/m/c-TCCA5BZN.js +2 -0
  143. package/dist/m/c-TQEMVJCS.js +1 -0
  144. package/dist/m/c-TQZ2JYHD.js +1 -0
  145. package/dist/m/c-TR6RMDQ7.js +3 -0
  146. package/dist/m/c-TRS6TM6O.js +1 -0
  147. package/dist/m/c-UGHH4MB3.js +1 -0
  148. package/dist/m/c-UKHTLWUW.js +1 -0
  149. package/dist/m/c-UL2YUY52.js +1 -0
  150. package/dist/m/c-UNN7LWCT.js +1 -0
  151. package/dist/m/c-UXBMRDZB.js +235 -0
  152. package/dist/m/c-V7PZZRG7.js +1 -0
  153. package/dist/m/c-VAK7SG37.js +1 -0
  154. package/dist/m/c-VDSS4K3Z.js +1 -0
  155. package/dist/m/c-VFX45IZQ.js +1 -0
  156. package/dist/m/c-VHNSBD2M.js +1 -0
  157. package/dist/m/c-VKEDWYDF.js +1 -0
  158. package/dist/m/c-VL52ADDR.js +1 -0
  159. package/dist/m/c-WN5AY3OZ.js +1 -0
  160. package/dist/m/c-WNYBLIOZ.js +1 -0
  161. package/dist/m/c-WRBKEJD4.js +1 -0
  162. package/dist/m/c-WWDBYXLK.js +1 -0
  163. package/dist/m/c-WYFDLITL.js +1 -0
  164. package/dist/m/c-XN6MBKI6.js +67 -0
  165. package/dist/m/c-XNNW2CWK.js +1 -0
  166. package/dist/m/c-XPELB6E5.js +1 -0
  167. package/dist/m/c-XQXZAO7N.js +1 -0
  168. package/dist/m/c-Y6USGTS5.js +5 -0
  169. package/dist/m/c-YDH7Q3NY.js +1 -0
  170. package/dist/m/c-YG2NJYS6.js +135 -0
  171. package/dist/m/c-Z36J63PP.js +1 -0
  172. package/dist/m/c-Z4VRC7GU.js +7 -0
  173. package/dist/m/c-Z6K52OLP.js +19 -0
  174. package/dist/m/c-ZDY2MDAA.js +21 -0
  175. package/dist/m/c-ZE46QDIH.js +1 -0
  176. package/dist/m/c-ZUCOKOHT.js +1 -0
  177. package/dist/m/core/admin-attrs-VGNLAUDK.js +1 -0
  178. package/dist/m/core/autosave-E4PEFPZ6.js +1 -0
  179. package/dist/m/core/conflict-notice-VQ6Y64PH.js +1 -0
  180. package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
  181. package/dist/m/core/host-permission-XN3K2IW3.js +1 -0
  182. package/dist/m/core/is-edit-mode-JXHDVGUL.js +1 -0
  183. package/dist/m/core/persist-HKHA66IY.js +3 -0
  184. package/dist/m/core/recovery-TBBSICE2.js +1 -0
  185. package/dist/m/core/save-WUCHTQBE.js +1 -0
  186. package/dist/m/core/save-core-FJVHT5M3.js +1 -0
  187. package/dist/m/core/snapshot-F3SWDZMM.js +1 -0
  188. package/dist/m/core/unsaved-warning-STKBHE6G.js +1 -0
  189. package/dist/m/dom/all-APS5ZSKP.js +1 -0
  190. package/dist/m/dom/dom-helpers-L7OSR6S4.js +1 -0
  191. package/dist/m/events/index-23DPTUT4.js +1 -0
  192. package/dist/m/internals/index-WOVAP3RU.js +1 -0
  193. package/dist/m/lib/cache-bust-CGWT6ZJI.js +1 -0
  194. package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
  195. package/dist/m/loader-HXX6YNH5.js +1 -0
  196. package/dist/m/manifest-F22DEDAC.js +1 -0
  197. package/dist/m/manifest-edit-C3C7376B.js +1 -0
  198. package/dist/m/manifest.json +3491 -0
  199. package/dist/m/options/options-2LH76JQ3.js +1 -0
  200. package/dist/m/page/editor-XUTB2HB2.js +189 -0
  201. package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +6 -0
  202. package/dist/m/plugins/bind-text-NX46N5UV.js +1 -0
  203. package/dist/m/plugins/cms-HP3LSTPN.js +2293 -0
  204. package/dist/m/plugins/demo-ZIBFAA3R.js +1 -0
  205. package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
  206. package/dist/m/plugins/indicator-PV4YW2ID.js +1 -0
  207. package/dist/m/plugins/live-NUTTXSBS.js +1 -0
  208. package/dist/m/plugins/menu-HCKO3DSP.js +138 -0
  209. package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
  210. package/dist/m/plugins/page-KSUH7G6J.js +2 -0
  211. package/dist/m/plugins/page-view-W73OW7WI.js +1 -0
  212. package/dist/m/plugins/people-AJJ32YCG.js +1 -0
  213. package/dist/m/plugins/quickcrop-QNY3PKLD.js +26 -0
  214. package/dist/m/plugins/quickcrop-QNY3PKLD.js.LEGAL.txt +7 -0
  215. package/dist/m/plugins/richclay-RC3L2F6X.js +517 -0
  216. package/dist/m/plugins/richclay-RC3L2F6X.js.LEGAL.txt +1 -0
  217. package/dist/m/plugins/sortable-YRMQX3ZU.js +1 -0
  218. package/dist/m/plugins/source-KHK5UUSK.js +1 -0
  219. package/dist/m/plugins/undo-OF2L3CXU.js +1 -0
  220. package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
  221. package/dist/m/plugins/upgrade-ZNYODP5Y.js +1 -0
  222. package/dist/m/plugins/upload-XSGNILUY.js +1 -0
  223. package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +21 -0
  224. package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +1 -0
  225. package/dist/m/plugins/wire-ZWVWKF76.js +1 -0
  226. package/dist/m/reader-core-ILWLXVMP.js +68 -0
  227. package/dist/m/route/route-KOCZSEGN.js +1 -0
  228. package/dist/m/sync/live-sync-4AUVXVJU.js +1 -0
  229. package/dist/m/ui/index-EVHKMKBL.js +1 -0
  230. package/dist/m/utils/index-4TJCVLT7.js +1 -0
  231. package/dist/m/vendor/Sortable.vendor-A755ITLN.js +3 -0
  232. package/dist/m/vendor/Sortable.vendor-A755ITLN.js.LEGAL.txt +1 -0
  233. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +6 -0
  234. package/dist/offline-manifest.json +3800 -0
  235. package/dist/page/themes/light-1.css +406 -0
  236. package/dist/server/core.cjs +23662 -22860
  237. package/dist/server/offline-runtime.cjs +4954 -0
  238. package/dist/server/offline-runtime.mjs +4923 -0
  239. package/dist/server/rooms.mjs +9 -0
  240. package/docs/guide.md +1078 -669
  241. package/entries/all.js +2 -0
  242. package/entries/clay-data.js +1 -1
  243. package/entries/clay-dom.js +2 -0
  244. package/entries/clay-events.js +2 -0
  245. package/entries/clay-internals.js +2 -0
  246. package/entries/clay-options.js +2 -0
  247. package/entries/clay-router.js +2 -0
  248. package/entries/clay-ui.js +2 -0
  249. package/entries/clay-utils.js +2 -0
  250. package/entries/clay.js +2 -0
  251. package/entries/sap.js +6 -6
  252. package/fonts/ibm-plex-mono-LICENSE +93 -0
  253. package/fonts/ibm-plex-mono-latin-300-normal.woff2 +0 -0
  254. package/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
  255. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  256. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  257. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  258. package/fonts/ibm-plex-sans-LICENSE +93 -0
  259. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  260. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  261. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  262. package/fonts/ibm-plex.css +71 -0
  263. package/fonts/inter-LICENSE +93 -0
  264. package/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  265. package/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  266. package/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  267. package/fonts/inter-greek-wght-normal.woff2 +0 -0
  268. package/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  269. package/fonts/inter-latin-wght-normal.woff2 +0 -0
  270. package/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  271. package/fonts/inter.css +62 -0
  272. package/fonts/newsreader-LICENSE +93 -0
  273. package/fonts/newsreader-latin-ext-opsz-normal.woff2 +0 -0
  274. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  275. package/fonts/newsreader-vietnamese-opsz-normal.woff2 +0 -0
  276. package/fonts/newsreader.css +26 -0
  277. package/fonts/work-sans-LICENSE +93 -0
  278. package/fonts/work-sans-latin-wght-normal.woff2 +0 -0
  279. package/fonts/work-sans.css +8 -0
  280. package/package.json +20 -3
  281. package/packed-contract.json +1 -0
  282. package/src/attrs/add-from.js +27 -4
  283. package/src/attrs/save-freeze.js +7 -52
  284. package/src/core/admin-attrs.js +119 -12
  285. package/src/core/admin-contenteditable.js +12 -10
  286. package/src/core/admin-inputs.js +14 -15
  287. package/src/core/admin-onclick.js +2 -0
  288. package/src/core/autosave.js +74 -21
  289. package/src/core/conflict-download.js +22 -0
  290. package/src/core/conflict-notice-ui.js +1173 -0
  291. package/src/core/conflict-notice.js +140 -697
  292. package/src/core/edit-mode.js +147 -14
  293. package/src/core/etag.js +30 -12
  294. package/src/core/host-meta.js +72 -8
  295. package/src/core/host-permission.js +29 -0
  296. package/src/core/is-edit-mode.js +145 -12
  297. package/src/core/mode-key.js +125 -0
  298. package/src/core/mode-lifecycle.js +544 -0
  299. package/src/core/mode-receipt.js +30 -0
  300. package/src/core/mode-refusal.js +13 -0
  301. package/src/core/mode-resources.js +52 -0
  302. package/src/core/mode-support.js +82 -0
  303. package/src/core/mode-switch.js +259 -0
  304. package/src/core/mode-transition.js +112 -0
  305. package/src/core/page-data-write.js +305 -0
  306. package/src/core/page-data.js +26 -227
  307. package/src/core/page-format-wire.js +4 -0
  308. package/src/core/page-format.js +58 -0
  309. package/src/core/persist.js +37 -9
  310. package/src/core/preserve-authored.js +114 -0
  311. package/src/core/recovery.js +772 -0
  312. package/src/core/runtime-url.js +105 -0
  313. package/src/core/save-core.js +109 -12
  314. package/src/core/save-replace.js +72 -0
  315. package/src/core/save-size-notice.js +134 -0
  316. package/src/core/save-size.js +140 -0
  317. package/src/core/save.js +805 -118
  318. package/src/core/snapshot.js +121 -11
  319. package/src/core/source-map.js +157 -9
  320. package/src/core/unsaved-warning.js +14 -4
  321. package/src/core/upgrade-run.js +408 -0
  322. package/src/core/version.js +18 -0
  323. package/src/lib/assets.js +30 -0
  324. package/src/lib/autosave-debug-gate.js +50 -0
  325. package/src/lib/autosave-scheduler.js +141 -0
  326. package/src/lib/boot-imports.js +8 -0
  327. package/src/lib/derived.js +3 -1
  328. package/src/lib/dirty-gate.js +15 -0
  329. package/src/lib/doc-storage.js +33 -0
  330. package/src/lib/effect.js +9 -4
  331. package/src/lib/mutation.js +4 -2
  332. package/src/lib/offline-runtime.js +226 -0
  333. package/src/lib/preload.js +48 -0
  334. package/src/lib/root-attrs.js +1 -1
  335. package/src/lib/trash.js +18 -0
  336. package/src/lib/undo/capture-move.js +152 -0
  337. package/src/live/local-channel.js +61 -0
  338. package/src/live/local-lane.js +60 -9
  339. package/src/live/server-lane.js +6 -3
  340. package/src/loader-logic.js +76 -15
  341. package/src/loader.js +218 -30
  342. package/src/page/adopt-boot.js +76 -22
  343. package/src/page/adopt.js +25 -4
  344. package/src/page/awareness.js +230 -0
  345. package/src/page/blocks.js +79 -5
  346. package/src/page/blocksel.js +20 -4
  347. package/src/page/board-face.js +13 -0
  348. package/src/page/board-panel.js +5 -17
  349. package/src/page/board-style.js +2 -13
  350. package/src/page/board-view.js +1 -11
  351. package/src/page/board-views.js +17 -0
  352. package/src/page/board.js +2 -2
  353. package/src/page/boot-pack.js +26 -0
  354. package/src/page/boot.js +237 -0
  355. package/src/page/checklist.js +1 -1
  356. package/src/page/chrome.js +411 -61
  357. package/src/page/csv-import.js +17 -2
  358. package/src/page/document-title-dom.js +40 -0
  359. package/src/page/document-title-runtime.js +76 -0
  360. package/src/page/document-title.js +9 -0
  361. package/src/page/drag.js +48 -13
  362. package/src/page/drop.js +2 -1
  363. package/src/page/editor.js +330 -86
  364. package/src/page/feature-loader.js +72 -0
  365. package/src/page/features/board.js +12 -0
  366. package/src/page/features/code.js +13 -0
  367. package/src/page/features/media.js +290 -0
  368. package/src/page/features/registry.js +23 -0
  369. package/src/page/features/table.js +383 -0
  370. package/src/page/{tool-view.js → features/tool.js} +33 -2
  371. package/src/page/format-codec.js +215 -0
  372. package/src/page/format-gate.js +179 -0
  373. package/src/page/format-level.js +157 -0
  374. package/src/page/highlight-slot.js +23 -0
  375. package/src/page/highlight.js +30 -16
  376. package/src/page/history-intent.js +78 -0
  377. package/src/page/inline.js +18 -25
  378. package/src/page/items.js +52 -11
  379. package/src/page/link.js +1 -0
  380. package/src/page/markdown.js +86 -1
  381. package/src/page/media-views.js +3 -0
  382. package/src/page/media.js +33 -245
  383. package/src/page/outline-cache.js +11 -0
  384. package/src/page/placeholder.js +27 -7
  385. package/src/page/projection.js +196 -50
  386. package/src/page/reader-patch.js +306 -0
  387. package/src/page/recover.js +1 -1
  388. package/src/page/rich/page-tool.js +1 -4
  389. package/src/page/runtime-owner.js +27 -0
  390. package/src/page/save-projection.js +7 -4
  391. package/src/page/schema.js +14 -0
  392. package/src/page/table-rect-key.js +3 -0
  393. package/src/page/table-select.js +4 -3
  394. package/src/page/table-style.js +1 -1
  395. package/src/page/table-views.js +3 -0
  396. package/src/page/table.js +8 -293
  397. package/src/page/themes/light-1.css +139 -2
  398. package/src/page/themes/light-1.source.css +269 -0
  399. package/src/page/tool-capability.js +3 -0
  400. package/src/page/tool-views.js +3 -0
  401. package/src/page/view.js +390 -107
  402. package/src/page/ybind/awareness.js +172 -0
  403. package/src/page/ybind/binding.js +9 -1
  404. package/src/page/ybind/bridge.js +50 -47
  405. package/src/page/ybind/decode.js +52 -19
  406. package/src/page/ybind/doc.js +27 -2
  407. package/src/page/ybind/group.js +12 -3
  408. package/src/page/ybind/index.js +2 -0
  409. package/src/page/ybind/lineage.js +71 -0
  410. package/src/page/ybind/marks.js +25 -1
  411. package/src/page/ybind/migrate.js +48 -13
  412. package/src/page/ybind/moves.js +32 -21
  413. package/src/page/ybind/positions.js +50 -0
  414. package/src/page/ybind/provider.js +84 -22
  415. package/src/page/ybind/recovery-proof.js +191 -0
  416. package/src/page/ybind/recovery-replay.js +130 -0
  417. package/src/page/ybind/tokens.js +15 -6
  418. package/src/page/ybind/walk.js +10 -3
  419. package/src/page/ybind/writer.js +48 -4
  420. package/src/plugins/ai-edit.js +167 -32
  421. package/src/plugins/bind-text.js +218 -0
  422. package/src/plugins/cms.js +93 -0
  423. package/src/plugins/indicator.js +57 -5
  424. package/src/plugins/live.js +282 -34
  425. package/src/plugins/menu/claygoo.svg.js +1 -0
  426. package/src/plugins/menu/seo-head.js +323 -0
  427. package/src/plugins/menu/seo.js +849 -0
  428. package/src/plugins/menu.js +425 -0
  429. package/src/plugins/page-view.js +131 -0
  430. package/src/plugins/page.js +1022 -275
  431. package/src/plugins/people.js +158 -31
  432. package/src/plugins/richclay.js +119 -9
  433. package/src/plugins/sortable.js +370 -60
  434. package/src/plugins/source.js +189 -366
  435. package/src/plugins/undo.js +289 -19
  436. package/src/plugins/unsent.js +132 -0
  437. package/src/plugins/upgrade-run.js +1 -0
  438. package/src/plugins/upgrade.js +210 -0
  439. package/src/plugins/upload-task.js +571 -0
  440. package/src/plugins/upload-ui.js +265 -0
  441. package/src/plugins/upload.js +34 -153
  442. package/src/plugins/visitor-drafts/inert.js +32 -0
  443. package/src/plugins/visitor-drafts/owner-chip.js +3 -0
  444. package/src/plugins/visitor-drafts/prompt.js +2 -2
  445. package/src/plugins/visitor-drafts-owner.js +85 -0
  446. package/src/plugins/visitor-drafts.js +31 -42
  447. package/src/plugins/wire.js +82 -1
  448. package/src/reader-core.js +26 -0
  449. package/src/recovery/conflicts.js +287 -0
  450. package/src/recovery/identity.js +154 -0
  451. package/src/recovery/store.js +792 -0
  452. package/src/recovery/undo.js +257 -0
  453. package/src/server/checkpoint/index.js +68 -4
  454. package/src/server/offline-runtime/index.js +214 -0
  455. package/src/server/rooms/index.js +2 -1
  456. package/src/server/rooms/live-data-rooms.js +2 -2
  457. package/src/server/rooms/page-admission.js +33 -0
  458. package/src/server/rooms/page-lineage.js +56 -0
  459. package/src/server/rooms/page-rebase.js +11 -5
  460. package/src/server/rooms/page-state.js +2 -0
  461. package/src/server/rooms/page-write-admission.js +9 -3
  462. package/src/source/session.js +705 -0
  463. package/src/sync/conflict-revert.js +5 -4
  464. package/src/sync/conflicts-edit.js +119 -0
  465. package/src/sync/conflicts-facade.js +49 -0
  466. package/src/sync/conflicts.js +99 -1
  467. package/src/sync/editing-marker.js +37 -6
  468. package/src/sync/editing-sender.js +8 -1
  469. package/src/sync/live-sync-edit.js +63 -0
  470. package/src/sync/live-sync.js +364 -119
  471. package/src/sync/local-presence.js +187 -0
  472. package/src/sync/merge-declarations.js +10 -0
  473. package/src/sync/merge-engine.js +12 -0
  474. package/src/sync/presence-store.js +412 -0
  475. package/src/sync/presence-target.js +243 -0
  476. package/src/sync/presence.js +72 -41
  477. package/src/sync/saved-handoff.js +200 -0
  478. package/src/sync/saved-stream.js +535 -0
  479. package/src/sync/version-guard.js +92 -0
  480. package/src/sync/wire-profile.js +81 -0
  481. package/src/ui/bevel-controls.js +2 -8
  482. package/src/ui/page-scheme.js +7 -0
  483. package/src/ui/skins/cms.js +1 -1
  484. package/src/ui/theme-config.js +150 -0
  485. package/src/ui/theme.js +6 -138
  486. package/src/ui/toast-lazy.js +11 -0
  487. package/src/vendor/LICENSES-optional.md +253 -0
  488. package/src/vendor/highlight.vendor.js +3 -0
  489. package/src/vendor/hljs-bash.vendor.js +2 -0
  490. package/src/vendor/hljs-css.vendor.js +2 -0
  491. package/src/vendor/hljs-javascript.vendor.js +2 -0
  492. package/src/vendor/hljs-json.vendor.js +2 -0
  493. package/src/vendor/hljs-python.vendor.js +2 -0
  494. package/src/vendor/hljs-xml.vendor.js +2 -0
  495. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  496. package/src/vendor/hyper-html-api-upgrade.vendor.js +5 -0
  497. package/src/vendor/hyper-morph-identity.vendor.js +2 -0
  498. package/src/vendor/hyper-morph-json.vendor.js +3 -0
  499. package/src/vendor/hyper-morph.vendor.js +6 -6
  500. package/src/vendor/hyper-undo.vendor.js +1 -1
  501. package/src/vendor/hypercms.vendor.js +169 -84
  502. package/src/vendor/marked.vendor.js +68 -0
  503. package/src/vendor/overtype.vendor.js +994 -0
  504. package/src/vendor/richclay.vendor.js +17 -17
  505. package/src/vendor/sap-core.vendor.js +5 -5
  506. package/src/vendor/sap-ownership.vendor.js +11 -7
@@ -10,12 +10,25 @@
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";
13
14
 
14
15
  export const ATOM = "";
15
16
  export const BREAK = '@{"type":"hard_break"}';
16
17
  const CODES = ["b", "i", "s", "c", "a"];
17
18
  const TAGS = { b: "strong", i: "em", s: "s", c: "code" };
18
19
  const NEST = ["a", "b", "i", "s", "c"];
20
+ const KNOWN = new Set(CODES);
21
+
22
+ // A block or an inline span the runtime has no schema for rides in an inert
23
+ // semantic envelope: canonical JSON, base64url, in one attribute. The block
24
+ // envelope holds the block record, the marks envelope the descriptors of one
25
+ // run, and the visible text stays the readable fallback.
26
+ export const OPAQUE = "data-clay-page-opaque";
27
+ export const VALUE = "data-clay-page-value";
28
+ export const MARKS = "data-clay-page-marks";
29
+ const ENVELOPE = "1";
30
+ const STRUCTURAL = new Set(["id", "t", "parent", "c", "m"]);
31
+ const knownType = (t) => Object.hasOwn(DEFAULTS, t);
19
32
 
20
33
  // Mirrors schema.js attrs minus id and pos (a test pins the two together).
21
34
  export const DEFAULTS = {
@@ -48,6 +61,67 @@ export function orderedIds(map) {
48
61
  });
49
62
  }
50
63
 
64
+ // ---- envelopes --------------------------------------------------------------
65
+
66
+ const b64url = (text) => {
67
+ let bin = "";
68
+ for (const byte of new TextEncoder().encode(text)) bin += String.fromCharCode(byte);
69
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
70
+ };
71
+
72
+ const fromB64url = (text) => {
73
+ const bin = atob(String(text).replace(/-/g, "+").replace(/_/g, "/"));
74
+ return new TextDecoder().decode(Uint8Array.from(bin, (ch) => ch.charCodeAt(0)));
75
+ };
76
+
77
+ // A key any object would take as its prototype rather than as data.
78
+ const hostile = (value) => {
79
+ if (value === null || typeof value !== "object") return false;
80
+ if (Array.isArray(value)) return value.some(hostile);
81
+ return Object.keys(value).some((key) => key === "__proto__" || hostile(value[key]));
82
+ };
83
+
84
+ // One envelope's JSON, under the codec's budget: deeper or larger refuses the
85
+ // whole source rather than cutting the envelope short.
86
+ function decodeJson(text) {
87
+ let value;
88
+ try { value = JSON.parse(fromB64url(text)); }
89
+ catch { throw new SyntaxError("page: an envelope is not readable canonical JSON"); }
90
+ try { bounded(value); }
91
+ catch (error) { throw new SyntaxError(error.message); }
92
+ if (hostile(value)) throw new SyntaxError("page: an envelope holds a prototype key");
93
+ return value;
94
+ }
95
+
96
+ // A block envelope: a record, whose own type and structural fields are kept
97
+ // apart from it.
98
+ function recordOf(text) {
99
+ const value = decodeJson(text);
100
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new SyntaxError("page: a block envelope is not a record");
101
+ if ("id" in value || "pos" in value) throw new SyntaxError("page: a block envelope holds a structural field");
102
+ if ("t" in value && (typeof value.t !== "string" || !value.t)) throw new SyntaxError("page: a block envelope names no type");
103
+ return value;
104
+ }
105
+
106
+ // A marks envelope: the descriptors of one run, validated and put back in the
107
+ // codec's canonical order however they arrived.
108
+ function entriesOfEnvelope(text) {
109
+ try { return normalizeEntries(decodeJson(text)); }
110
+ catch (error) { throw error instanceof SyntaxError ? error : new SyntaxError(error.message); }
111
+ }
112
+
113
+ // The entries of a marks envelope as it arrived, validated by the same reader
114
+ // the article uses.
115
+ export const marksEntries = (text) => entriesOfEnvelope(text);
116
+
117
+ const envelopeOf = (value) => ` ${OPAQUE}="${ENVELOPE}" ${VALUE}="${escAttr(b64url(canon(value)))}"`;
118
+ const marksEnvelope = (entries) => (entries.length ? ` ${MARKS}="${escAttr(b64url(JSON.stringify(entries)))}"` : "");
119
+
120
+ // The same two envelopes as attribute maps, for the internal rich clipboard,
121
+ // which writes them onto its own elements instead of an article line.
122
+ export const envelopeAttrs = (record) => ({ [OPAQUE]: ENVELOPE, [VALUE]: b64url(canon(record)) });
123
+ export const marksAttrs = (entries) => ({ [MARKS]: b64url(JSON.stringify(normalizeEntries(entries))) });
124
+
51
125
  // ---- marks ------------------------------------------------------------------
52
126
 
53
127
  // inline.js unitsOf: runs [[n, key]] or per-unit keys -> one key per unit, or null.
@@ -72,28 +146,29 @@ export function runsOf(units) {
72
146
  return out;
73
147
  }
74
148
 
75
- // A mark key as { b: true, a: ["href"] ... }; null for an atom key.
149
+ // A mark key as { marks: { b: true, a: ["href"] ... }, entries: [...] }, the
150
+ // entries being the descriptors of every mark this runtime has no code for, in
151
+ // the codec's canonical order.
76
152
  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;
153
+ const marks = {};
154
+ if (!key || key[0] === "@") return { marks, entries: [] };
155
+ const opaque = [];
156
+ for (const desc of descriptors(key)) {
157
+ if (desc.name === undefined && KNOWN.has(desc.code)) marks[desc.code] = desc.attrs ?? true;
158
+ else opaque.push(desc);
93
159
  }
94
- return out;
160
+ return { marks, entries: opaque.length ? entriesOf(opaque) : [] };
95
161
  }
96
162
 
163
+ // One unit's key, as the record keeps it: the codes this runtime knows, then
164
+ // every descriptor it keeps for a newer runtime.
165
+ const unitKey = (key, text, i) => {
166
+ if (!key) return "";
167
+ if (key[0] === "@") return key === BREAK && text[i] === ATOM ? key : "";
168
+ const { marks, entries } = marksOfKey(key);
169
+ return keyOfMarks(safeMarks(marks), entries);
170
+ };
171
+
97
172
  // A link to a script URL is never kept: it would run on the page's origin when a
98
173
  // reader clicks it in the saved article.
99
174
  const SCRIPT_URL = /^(javascript|vbscript|data):/i;
@@ -102,14 +177,14 @@ const safeMarks = (marks) => {
102
177
  return marks;
103
178
  };
104
179
 
105
- function keyOfMarks(marks) {
180
+ function keyOfMarks(marks, entries = []) {
106
181
  let key = "";
107
182
  for (const code of CODES) {
108
183
  if (!marks[code]) continue;
109
184
  key += code;
110
185
  if (Array.isArray(marks[code])) key += JSON.stringify(marks[code]);
111
186
  }
112
- return key;
187
+ return entries.length ? key + keyOf(entries) : key;
113
188
  }
114
189
 
115
190
  // ---- text normalization -----------------------------------------------------
@@ -175,7 +250,7 @@ function layout(blocks) {
175
250
  let prev = null;
176
251
  for (const id of orderedIds(blocks)) {
177
252
  const b = blocks[id];
178
- const t = DEFAULTS[b.t] ? b.t : null;
253
+ const t = typeof b.t === "string" && b.t ? b.t : null;
179
254
  let parent = b.parent ?? null;
180
255
  if (parent !== null) {
181
256
  const at = stack.lastIndexOf(parent);
@@ -207,7 +282,7 @@ const columnWidth = (value) => (typeof value === "number" && Number.isFinite(val
207
282
  function cellModel(value) {
208
283
  if (value === null || typeof value !== "object" || typeof value.c !== "string") return normalizeText(cellText(value), null).c;
209
284
  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)))));
285
+ const units = raw && raw.map((k, i) => unitKey(k, value.c, i));
211
286
  if (!units) return normalizeText(cellText(value), null).c;
212
287
  const n = normalizeText(value.c, units);
213
288
  const fields = textFields(n.c, n.units);
@@ -240,10 +315,44 @@ function projectBoard(b) {
240
315
  };
241
316
  }
242
317
 
318
+ // The record a block envelope carries: its own type first, then every field the
319
+ // runtime keeps for the block, extras included.
320
+ const recordOfItem = (item) => {
321
+ const { t, parent: _p, id: _i, ...fields } = item;
322
+ return { t, ...fields };
323
+ };
324
+
325
+ // The fields of a known block the runtime has no schema attribute for. They are
326
+ // merged back only when serialized, inside the inert payload.
327
+ const extrasOfItem = (item) => {
328
+ const out = {};
329
+ for (const [k, v] of Object.entries(item)) if (!STRUCTURAL.has(k) && !(knownType(item.t) && Object.hasOwn(DEFAULTS[item.t], k))) out[k] = v;
330
+ return Object.keys(out).length ? envelopeOf(out) : "";
331
+ };
332
+
333
+ const extrasOfBlock = (t, b) => {
334
+ const out = {};
335
+ for (const [k, v] of Object.entries(b)) if (!STRUCTURAL.has(k) && k !== "pos" && !Object.hasOwn(DEFAULTS[t], k)) out[k] = v;
336
+ return out;
337
+ };
338
+
243
339
  function projectOne({ id, b, t, parent, indent }) {
244
340
  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];
341
+ if (!knownType(t)) {
342
+ const { t: _t, id: _id, pos: _pos, parent: _p, ...rest } = b;
343
+ // The text and its marks are kept as they stand, but marks in the older
344
+ // per-unit form come back as runs, so one record has one revision whichever
345
+ // form it was written in.
346
+ if (typeof rest.c === "string" && rest.m !== undefined) {
347
+ const units = unitsOf(rest.m, rest.c.length);
348
+ if (units && units.some(Boolean)) rest.m = runsOf(units);
349
+ else delete rest.m;
350
+ }
351
+ return { id, t, ...(parent ? { parent } : {}), ...rest };
352
+ }
353
+ const { t: _t, id: _id, pos, c, m, rows, cols, columns, cards, parent: _p, indent: _i, ...attrs } = b;
354
+ const extras = extrasOfBlock(t, attrs);
355
+ for (const k of Object.keys(extras)) delete attrs[k];
247
356
  const fields = { ...attrs, parent };
248
357
  if (t === "list_item") fields.indent = indent;
249
358
  if (t === "heading") fields.level = clampLevel(attrs.level ?? 2);
@@ -253,13 +362,13 @@ function projectOne({ id, b, t, parent, indent }) {
253
362
  if (t === "chart" && typeof fields.title === "string") fields.title = normalizeText(fields.title, null).c;
254
363
  if (t === "table") Object.assign(fields, projectTable(b));
255
364
  else if (t === "board") Object.assign(fields, projectBoard(b));
256
- const item = { id, t, ...omitDefaults(t, fields) };
365
+ const item = { id, t, ...omitDefaults(t, fields), ...extras };
257
366
  if (t === "table") { item.cols = fields.cols; item.rows = fields.rows; }
258
367
  if (t === "board") { item.columns = fields.columns; item.cards = fields.cards; }
259
368
  if (TEXT.has(t)) {
260
369
  const text = typeof c === "string" ? c : "";
261
370
  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)))));
371
+ const units = raw && raw.map((k, i) => unitKey(k, text, i));
263
372
  if (t === "code_block") Object.assign(item, { c: text.replace(/\r\n?/g, "\n") });
264
373
  else {
265
374
  const n = normalizeText(text, units);
@@ -301,7 +410,7 @@ const escText = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>
301
410
  const escAttr = (s) => escText(String(s)).replace(/"/g, "&quot;");
302
411
  const attr = (name, value) => (value === null || value === undefined ? "" : ` ${name}="${escAttr(value)}"`);
303
412
 
304
- export function renderInline(c, m) {
413
+ export function renderInline(c, m, marksEnvelopes = true) {
305
414
  const units = unitsOf(m, c.length) || [];
306
415
  let html = "";
307
416
  const open = [];
@@ -316,12 +425,13 @@ export function renderInline(c, m) {
316
425
  }
317
426
  let j = i + 1;
318
427
  while (j < c.length && (units[j] || "") === key && !(c[j] === ATOM && key[0] === "@")) j++;
319
- const marks = marksOfKey(key);
428
+ const { marks, entries } = marksOfKey(key);
320
429
  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 }));
430
+ if (entries.length && marksEnvelopes) want.push({ sig: `marks${canon(entries)}`, tag: "span", entries });
321
431
  let keep = 0;
322
432
  while (keep < open.length && keep < want.length && open[keep].sig === want[keep].sig) keep++;
323
433
  closeTo(keep);
324
- for (const w of want.slice(keep)) { html += w.tag === "a" ? `<a${attr("href", w.href)}>` : `<${w.tag}>`; open.push(w); }
434
+ for (const w of want.slice(keep)) { html += w.entries ? `<span${marksEnvelope(w.entries)}>` : w.tag === "a" ? `<a${attr("href", w.href)}>` : `<${w.tag}>`; open.push(w); }
325
435
  html += escText(c.slice(i, j));
326
436
  i = j;
327
437
  }
@@ -338,26 +448,28 @@ const dataAttrs = (obj) => Object.entries(obj).map(([k, v]) => attr(`data-${k}`,
338
448
 
339
449
  function renderLeaf(item, clean, tables) {
340
450
  const id = attr("data-id", item.id);
451
+ if (!knownType(item.t)) return `<div${id}${envelopeOf(recordOfItem(item))}>${renderInline(item.c ?? "", item.m, false)}</div>`;
452
+ const at = id + extrasOfItem(item);
341
453
  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>`;
454
+ case "paragraph": return `<p${at}>${inlineOf(item)}</p>`;
455
+ case "heading": { const h = `h${item.level ?? 2}`; return `<${h}${at}>${inlineOf(item)}</${h}>`; }
456
+ case "callout": return `<aside${at} class="page-callout">${inlineOf(item)}</aside>`;
457
+ case "code_block": return `<pre${at}${attr("data-lang", item.lang || null)}><code>${escText(item.c ?? "")}</code></pre>`;
458
+ case "divider": return `<hr${at}>`;
459
+ 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
460
  case "file": {
349
461
  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>`;
462
+ 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
463
  }
352
464
  case "table": {
353
465
  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
466
  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>`;
467
+ 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
468
  }
357
469
  case "chart": {
358
470
  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
471
  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>`;
472
+ return `<figure${at} data-type="chart"${data}>${renderChartSvg(chart, { width: 640, height: 320 })}<figcaption>${escText(item.title ?? "")}</figcaption></figure>`;
361
473
  }
362
474
  case "board": {
363
475
  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 +477,16 @@ function renderLeaf(item, clean, tables) {
365
477
  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
478
  return `<div${attr("data-col", col.id)}${col.known ? "" : " data-missing"}>${col.known ? `<h4>${escText(col.name)}</h4>` : ""}<ul>${cards}</ul></div>`;
367
479
  });
368
- return `<section${id} data-type="board">\n${parts.join("\n")}\n</section>`;
480
+ return `<section${at} data-type="board">\n${parts.join("\n")}\n</section>`;
369
481
  }
370
- case "tool": return `<section${id} data-type="tool">${toolData(item)}</section>`;
371
- case "preserved": return preservedHtml(item, clean);
482
+ case "tool": return `<section${at} data-type="tool">${toolData(item)}</section>`;
483
+ case "preserved": return preservedHtml(item, clean, at);
372
484
  default: return "";
373
485
  }
374
486
  }
375
487
 
376
488
  // 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>`;
489
+ const preservedHtml = (item, clean, id = attr("data-id", item.id)) => `<div${id} data-type="preserved">${clean.preserved(item.html || "")}</div>`;
378
490
 
379
491
  const RAW = { preserved: (html) => html, fallback: (html) => html };
380
492
 
@@ -423,11 +535,11 @@ export function renderModel(model, { fallbackOf = () => "", clean = RAW } = {})
423
535
  }
424
536
  if (lists.length < d + 1) { add(`<${tag}>`); lists.push(tag); }
425
537
  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)}`);
538
+ 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
539
  continue;
428
540
  }
429
541
  if (item.t === "toggle") {
430
- add(`<details${attr("data-id", item.id)}${item.open ? " open" : ""}><summary>${inlineOf(item)}</summary>`);
542
+ add(`<details${attr("data-id", item.id)}${extrasOfItem(item)}${item.open ? " open" : ""}><summary>${inlineOf(item)}</summary>`);
431
543
  stack.push(item.id);
432
544
  continue;
433
545
  }
@@ -453,12 +565,12 @@ const INLINE_TAGS = new Set(["A", "ABBR", "B", "BDI", "BDO", "BR", "CITE", "CODE
453
565
  function inlineOfEl(el, skip = () => false) {
454
566
  let c = "";
455
567
  const units = [];
456
- const walk = (node, marks) => {
568
+ const walk = (node, marks, entries) => {
457
569
  for (const child of node.childNodes) {
458
570
  if (skip(child)) continue;
459
571
  if (child.nodeType === 3) {
460
572
  c += child.data;
461
- const key = keyOfMarks(safeMarks({ ...marks }));
573
+ const key = keyOfMarks(safeMarks({ ...marks }), entries);
462
574
  for (let i = 0; i < child.data.length; i++) units.push(key);
463
575
  continue;
464
576
  }
@@ -467,20 +579,44 @@ function inlineOfEl(el, skip = () => false) {
467
579
  if (tag === "br") { c += ATOM; units.push(BREAK); continue; }
468
580
  if (tag === "span" && child.hasAttribute("data-atom")) { c += ATOM; units.push(child.getAttribute("data-atom")); continue; }
469
581
  const next = { ...marks };
582
+ if (tag === "span" && child.hasAttribute(MARKS)) {
583
+ walk(child, next, normalizeEntries([...entries, ...entriesOfEnvelope(child.getAttribute(MARKS))]));
584
+ continue;
585
+ }
470
586
  if (tag === "strong" || tag === "b") next.b = true;
471
587
  else if (tag === "em" || tag === "i") next.i = true;
472
588
  else if (tag === "s" || tag === "del" || tag === "strike") next.s = true;
473
589
  else if (tag === "code") next.c = true;
474
590
  else if (tag === "a" && child.hasAttribute("href")) next.a = [child.getAttribute("href")];
475
- walk(child, next);
591
+ walk(child, next, entries);
476
592
  }
477
593
  };
478
- walk(el, {});
594
+ walk(el, {}, []);
479
595
  return normalizeText(c, units);
480
596
  }
481
597
 
482
598
  const text = (el) => normalizeText(el ? el.textContent : "", null).c;
483
599
 
600
+ // The block envelope an element carries, or null. A malformed, oversized or
601
+ // hostile one refuses the whole article: it never falls back to plain text.
602
+ const envelopeAt = (el) => {
603
+ if (!el.hasAttribute(OPAQUE)) return null;
604
+ if (el.getAttribute(OPAQUE) !== ENVELOPE) throw new SyntaxError(`page: unknown envelope version ${el.getAttribute(OPAQUE)}`);
605
+ const text = el.getAttribute(VALUE);
606
+ if (text === null) throw new SyntaxError("page: a block envelope holds no value");
607
+ return recordOf(text);
608
+ };
609
+
610
+ // The record a block envelope carries, validated; null when the element carries
611
+ // no envelope at all.
612
+ export const envelopeRecord = (el) => envelopeAt(el);
613
+
614
+ // The extras a known element's envelope adds to it.
615
+ const extrasAt = (el) => {
616
+ const value = envelopeAt(el);
617
+ return value && typeof value.t !== "string" ? value : null;
618
+ };
619
+
484
620
  export function parseArticle(article) {
485
621
  const items = [];
486
622
  const walk = (container, parent) => {
@@ -506,7 +642,10 @@ export function parseArticle(article) {
506
642
  flushLoose();
507
643
  };
508
644
  const idOf = (el) => el.getAttribute("data-id") || null;
509
- const push = (el, t, parent, fields) => items.push({ id: idOf(el), t, parent, ...fields });
645
+ const push = (el, t, parent, fields) => {
646
+ const extra = extrasAt(el);
647
+ items.push({ id: idOf(el), t, parent, ...(extra ? { ...extra, ...fields } : fields) });
648
+ };
510
649
  const list = (el, parent, depth) => {
511
650
  for (const li of el.children) {
512
651
  if (li.tagName !== "LI") continue;
@@ -515,13 +654,20 @@ export function parseArticle(article) {
515
654
  const n = inlineOfEl(li, (child) => child === box || nested.includes(child));
516
655
  const kind = box ? "check" : el.tagName === "OL" ? "ordered" : "bullet";
517
656
  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) });
657
+ items.push({ id: idOf(li), t: "list_item", ...omitDefaults("list_item", fields), ...(extrasAt(li) ?? {}), ...textFields(n.c, n.units) });
519
658
  for (const sub of nested) list(sub, parent, depth + 1);
520
659
  }
521
660
  };
522
661
  const blockOf = (el, parent) => {
523
662
  const tag = el.tagName;
524
663
  const P = { parent };
664
+ const record = envelopeAt(el);
665
+ if (record && typeof record.t === "string") {
666
+ if (knownType(record.t) || record.t === "opaque_block") throw new SyntaxError(`page: an envelope names the known block type ${record.t}`);
667
+ const { t, ...fields } = record;
668
+ items.push({ id: idOf(el), t, parent, ...fields });
669
+ return;
670
+ }
525
671
  const onlyImage = tag === "P" && el.children.length === 1 && el.children[0].tagName === "IMG" && !el.textContent.trim();
526
672
  if (tag === "P" && !onlyImage) return push(el, "paragraph", parent, { ...omitDefaults("paragraph", P), ...tf(el) });
527
673
  if (/^H[1-6]$/.test(tag)) return push(el, "heading", parent, { ...omitDefaults("heading", { ...P, level: +tag[1] }), ...tf(el) });