@panphora/clayjs 1.16.0 → 1.18.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 (511) hide show
  1. package/dist/all.js +1 -1
  2. package/dist/clay-dom.js +1 -1
  3. package/dist/clay-events.js +1 -1
  4. package/dist/clay-internals.js +1 -1
  5. package/dist/clay-options.js +1 -1
  6. package/dist/clay-router.js +1 -1
  7. package/dist/clay-ui.js +1 -1
  8. package/dist/clay-utils.js +1 -1
  9. package/dist/clay.js +1 -1
  10. package/dist/clay.standalone.js +72519 -59839
  11. package/dist/m/attrs/refetch-on-save-ZUE7PSCI.js +1 -0
  12. package/dist/m/attrs/save-freeze-LWGF6XUN.js +1 -0
  13. package/dist/m/c-2GRUM77Q.js +30 -0
  14. package/dist/m/c-2KIEGNC4.js +1 -0
  15. package/dist/m/{c-5R4PTRG2.js → c-2KL3BD52.js} +1 -1
  16. package/dist/m/c-2YYDMLNZ.js +1 -0
  17. package/dist/m/c-2ZZBC45O.js +1 -0
  18. package/dist/m/c-372N6H3B.js +1 -0
  19. package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
  20. package/dist/m/c-3LDGIBMN.js +1 -0
  21. package/dist/m/c-3O7OBDCM.js +1 -0
  22. package/dist/m/c-4ASQPRQL.js +1 -0
  23. package/dist/m/c-4TXNLX4K.js +6 -0
  24. package/dist/m/c-4YU3BR4D.js +6 -0
  25. package/dist/m/c-5734MF2P.js +1 -0
  26. package/dist/m/c-57XDY6HC.js +1 -0
  27. package/dist/m/{c-VFX45IZQ.js → c-5B4YBZXC.js} +1 -1
  28. package/dist/m/c-5ONTYIUI.js +1 -0
  29. package/dist/m/c-5OOVFCJM.js +1 -0
  30. package/dist/m/c-5T6NZHNM.js +1 -0
  31. package/dist/m/{c-YDH7Q3NY.js → c-5ZQV7KC4.js} +1 -1
  32. package/dist/m/{c-Z36J63PP.js → c-6DB2OLRJ.js} +1 -1
  33. package/dist/m/c-6DNRCPNM.js +1 -0
  34. package/dist/m/c-6UGWFMQC.js +2 -0
  35. package/dist/m/c-7K4SCQB7.js +1 -0
  36. package/dist/m/c-7MG5RU63.js +1 -0
  37. package/dist/m/c-7PRJCA3E.js +1 -0
  38. package/dist/m/c-A6L3FMUS.js +1 -0
  39. package/dist/m/c-AMU3BPPR.js +4 -0
  40. package/dist/m/c-AMYEFA2E.js +1 -0
  41. package/dist/m/c-AQYCG7QW.js +1 -0
  42. package/dist/m/c-ATKAOH23.js +1 -0
  43. package/dist/m/c-B3JM5ZXC.js +1 -0
  44. package/dist/m/c-B5CS55WR.js +1 -0
  45. package/dist/m/c-B5UJQT7W.js +1 -0
  46. package/dist/m/c-B7J4K2AE.js +3 -0
  47. package/dist/m/c-BHZYSHW4.js +1 -0
  48. package/dist/m/c-BIJALSRD.js +1 -0
  49. package/dist/m/c-BJ7YQ5QZ.js +1 -0
  50. package/dist/m/c-BPF2NSPV.js +1 -0
  51. package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
  52. package/dist/m/c-BUAYFEV5.js +1 -0
  53. package/dist/m/c-C6IPRG34.js +1 -0
  54. package/dist/m/{c-2JL6E75N.js → c-CAS3NND4.js} +1 -1
  55. package/dist/m/{c-4DRMS4QG.js → c-CCQ25GFY.js} +1 -1
  56. package/dist/m/c-CJFDLD43.js +1 -0
  57. package/dist/m/c-COPTL5WB.js +1 -0
  58. package/dist/m/c-D3E4WJ4M.js +166 -0
  59. package/dist/m/c-D5IKEKXW.js +7 -0
  60. package/dist/m/c-DHBRA6DL.js +1 -0
  61. package/dist/m/c-DKLS74XB.js +72 -0
  62. package/dist/m/c-DVYGFTSK.js +3 -0
  63. package/dist/m/c-E3ZXARWA.js +1 -0
  64. package/dist/m/c-EK63G3CG.js +1 -0
  65. package/dist/m/c-ERQSNR6K.js +13 -0
  66. package/dist/m/c-EVYAISP3.js +1 -0
  67. package/dist/m/c-EXKHPOVC.js +3 -0
  68. package/dist/m/{c-7VGZ7RU5.js → c-FWKHM4E7.js} +1 -1
  69. package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
  70. package/dist/m/c-GNDL3TKB.js +1 -0
  71. package/dist/m/c-GRWMD6O5.js +1 -0
  72. package/dist/m/c-GRXHHFPO.js +1 -0
  73. package/dist/m/{c-OYTFP5QN.js → c-GY422N3Q.js} +1 -1
  74. package/dist/m/c-HAWFNBYG.js +1 -0
  75. package/dist/m/c-HG2C77BM.js +4 -0
  76. package/dist/m/c-HHGJBU4D.js +1 -0
  77. package/dist/m/c-HMV6P4RX.js +1 -0
  78. package/dist/m/c-HMYWIHFG.js +1 -0
  79. package/dist/m/c-HT2DKBV2.js +1 -0
  80. package/dist/m/c-I2SCCUEW.js +1 -0
  81. package/dist/m/{c-HRDCD3WN.js → c-ICJYHABF.js} +2 -2
  82. package/dist/m/c-IMZCWYHR.js +7 -0
  83. package/dist/m/c-IVMD3FEM.js +1 -0
  84. package/dist/m/c-IVTRVENZ.js +1 -0
  85. package/dist/m/c-IW6GGOU2.js +1 -0
  86. package/dist/m/c-JKZJIUIP.js +1 -0
  87. package/dist/m/{c-NZMNXVF6.js → c-JNLX3RJB.js} +1 -1
  88. package/dist/m/c-JRKY44DK.js +1 -0
  89. package/dist/m/c-K3ADYNLU.js +1 -0
  90. package/dist/m/c-KBS3UV6B.js +1 -0
  91. package/dist/m/c-KGHVFRR3.js +1 -0
  92. package/dist/m/c-KJJPV77R.js +1 -0
  93. package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
  94. package/dist/m/{c-5Y6TNX5W.js → c-KP64LT2A.js} +2 -2
  95. package/dist/m/c-KQ7E3PYY.js +1 -0
  96. package/dist/m/c-L3F5ZLIV.js +1 -0
  97. package/dist/m/{c-LR4CGEPT.js → c-L42OLXWM.js} +3 -2
  98. package/dist/m/c-L5VFATT6.js +1 -0
  99. package/dist/m/c-L7JL5TL3.js +3 -0
  100. package/dist/m/c-LKSL4AUJ.js +1 -0
  101. package/dist/m/c-M6FNQCTQ.js +1 -0
  102. package/dist/m/c-MCOOHCSG.js +1 -0
  103. package/dist/m/c-MHI3GYKF.js +1 -0
  104. package/dist/m/c-MWXFLW4J.js +1 -0
  105. package/dist/m/c-NEL7HQW3.js +1 -0
  106. package/dist/m/c-NQJRVJ56.js +1 -0
  107. package/dist/m/c-NWX3MFT4.js +1 -0
  108. package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
  109. package/dist/m/c-OKXI52N7.js +1 -0
  110. package/dist/m/{c-MMUIUUZG.js → c-OPLUZR3A.js} +1 -1
  111. package/dist/m/{c-J7CA54IP.js → c-OTMKN2OO.js} +1 -1
  112. package/dist/m/{c-LTGMXXKO.js → c-OYRZ7XG2.js} +1 -1
  113. package/dist/m/c-P3OFYUMA.js +1 -0
  114. package/dist/m/c-PLN76GF3.js +6 -0
  115. package/dist/m/c-PLP4JKFC.js +1 -0
  116. package/dist/m/c-Q26MME6N.js +1 -0
  117. package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
  118. package/dist/m/{c-SWRJFCNN.js → c-QIKT6SKM.js} +1 -1
  119. package/dist/m/c-QIXT3Q7C.js +28 -0
  120. package/dist/m/c-QO4HNGCQ.js +1 -0
  121. package/dist/m/c-R27DWQKX.js +1 -0
  122. package/dist/m/{c-HVYXIS72.js → c-R2XUODVY.js} +1 -1
  123. package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
  124. package/dist/m/c-RND3G2S2.js +1 -0
  125. package/dist/m/c-S2U6NJU4.js +1 -0
  126. package/dist/m/c-SPZPGD3R.js +1 -0
  127. package/dist/m/c-SW4YEG2D.js +8 -0
  128. package/dist/m/c-SZGYW5HT.js +1 -0
  129. package/dist/m/c-T637T7SN.js +1 -0
  130. package/dist/m/c-TJAXGIRV.js +1 -0
  131. package/dist/m/{c-KKWLDOPM.js → c-TNEM3RNM.js} +1 -1
  132. package/dist/m/c-TTB5EK4S.js +32 -0
  133. package/dist/m/c-U7SHNWDH.js +1 -0
  134. package/dist/m/c-UDHIVLYH.js +20 -0
  135. package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
  136. package/dist/m/{c-TQZ2JYHD.js → c-UORXLSD3.js} +1 -1
  137. package/dist/m/c-UPD33ZPE.js +1 -0
  138. package/dist/m/c-URSMSFCB.js +1 -0
  139. package/dist/m/c-UY4QX42B.js +1 -0
  140. package/dist/m/c-V44KWI2L.js +4 -0
  141. package/dist/m/c-VG5XIN4T.js +1 -0
  142. package/dist/m/c-VILENZTD.js +1 -0
  143. package/dist/m/{c-33HUYEDI.js → c-VJYEVRYK.js} +1 -1
  144. package/dist/m/c-VR2BJ4AN.js +1 -0
  145. package/dist/m/c-W4JCVLAB.js +1 -0
  146. package/dist/m/c-W6DQSHXI.js +135 -0
  147. package/dist/m/c-WBGWGOM7.js +1 -0
  148. package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
  149. package/dist/m/c-WGW764WX.js +1 -0
  150. package/dist/m/c-WMRJKQ2V.js +5 -0
  151. package/dist/m/c-WNTYKCDG.js +1 -0
  152. package/dist/m/c-WQWIRWO3.js +72 -0
  153. package/dist/m/c-X5LNHET5.js +1 -0
  154. package/dist/m/c-X5TSXZYJ.js +2 -0
  155. package/dist/m/c-XGH4GS2T.js +1 -0
  156. package/dist/m/c-Y6F2FORL.js +1 -0
  157. package/dist/m/c-YBKHUAK7.js +10 -0
  158. package/dist/m/c-YBSS55KU.js +20 -0
  159. package/dist/m/c-YCLU4UNT.js +2 -0
  160. package/dist/m/c-YVZ2L7CE.js +1 -0
  161. package/dist/m/c-ZK4GIZH2.js +1 -0
  162. package/dist/m/c-ZRXJD4G6.js +1 -0
  163. package/dist/m/c-ZWBI4HYK.js +1 -0
  164. package/dist/m/{c-HT33EQRN.js → c-ZWIOJGMT.js} +1 -1
  165. package/dist/m/core/admin-attrs-YY7T5FGU.js +1 -0
  166. package/dist/m/core/autosave-S6OMNDCK.js +1 -0
  167. package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
  168. package/dist/m/core/host-permission-6EVJGNPL.js +1 -0
  169. package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
  170. package/dist/m/core/{persist-HKHA66IY.js → persist-UKXV5KLA.js} +1 -1
  171. package/dist/m/core/recovery-DRFZRJED.js +1 -0
  172. package/dist/m/core/save-LYY7K6U7.js +1 -0
  173. package/dist/m/core/save-core-MKRPSEEO.js +1 -0
  174. package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
  175. package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
  176. package/dist/m/events/index-RN2JNGBV.js +1 -0
  177. package/dist/m/internals/index-TGLQINN3.js +1 -0
  178. package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
  179. package/dist/m/loader-3BBNZZGM.js +1 -0
  180. package/dist/m/manifest-F449FF98.js +1 -0
  181. package/dist/m/manifest-edit-4396A009.js +1 -0
  182. package/dist/m/manifest.json +2509 -1474
  183. package/dist/m/options/options-EJMS3K24.js +1 -0
  184. package/dist/m/page/editor-6LRB3GE4.js +304 -0
  185. package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
  186. package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
  187. package/dist/m/plugins/demo-OYO5GCAO.js +1 -0
  188. package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
  189. package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
  190. package/dist/m/plugins/live-AD4H6XSL.js +1 -0
  191. package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
  192. package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
  193. package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
  194. package/dist/m/plugins/people-JCHVPF7P.js +1 -0
  195. package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
  196. package/dist/m/plugins/{richclay-RC3L2F6X.js → richclay-KLGETK77.js} +2 -2
  197. package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
  198. package/dist/m/plugins/source-TJXOIOYY.js +1 -0
  199. package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
  200. package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
  201. package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
  202. package/dist/m/plugins/upload-NHGJXZK2.js +1 -0
  203. package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
  204. package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
  205. package/dist/m/plugins/wire-VBWXEXU2.js +1 -0
  206. package/dist/m/reader-core-4W2KYEWQ.js +77 -0
  207. package/dist/m/route/route-62CHXL5B.js +1 -0
  208. package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
  209. package/dist/m/ui/index-IVWN2QC4.js +1 -0
  210. package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
  211. package/dist/offline-manifest.json +1560 -889
  212. package/dist/page/themes/light-1.css +33 -8
  213. package/dist/server/core.cjs +1554 -311
  214. package/dist/server/offline-runtime.cjs +63 -63
  215. package/dist/server/offline-runtime.mjs +63 -63
  216. package/dist/server/page-compaction-worker.cjs +3710 -0
  217. package/dist/server/rooms.mjs +5 -0
  218. package/docs/guide.md +653 -101
  219. package/entries/sap.js +5 -5
  220. package/package.json +1 -1
  221. package/src/core/admin-contenteditable.js +5 -2
  222. package/src/core/admin-inputs.js +1 -0
  223. package/src/core/admin-onclick.js +2 -1
  224. package/src/core/boot-phase.js +2 -1
  225. package/src/core/conflict-download.js +3 -29
  226. package/src/core/conflict-notice-ui.js +4 -7
  227. package/src/core/download.js +32 -0
  228. package/src/core/host-meta.js +56 -0
  229. package/src/core/is-edit-mode.js +17 -1
  230. package/src/core/mode-key.js +125 -19
  231. package/src/core/mode-support.js +5 -0
  232. package/src/core/mode-transition.js +20 -6
  233. package/src/core/preserve-authored.js +4 -4
  234. package/src/core/recovery.js +121 -33
  235. package/src/core/save-accepted.js +33 -0
  236. package/src/core/save-core.js +111 -32
  237. package/src/core/save-job.js +108 -0
  238. package/src/core/save-prepare.js +53 -0
  239. package/src/core/save-size-notice.js +3 -1
  240. package/src/core/save-slices.js +15 -0
  241. package/src/core/save.js +557 -127
  242. package/src/core/serialize-steps.js +109 -0
  243. package/src/core/snapshot.js +209 -40
  244. package/src/core/source-map.js +83 -38
  245. package/src/core/stale-host-notice.js +1 -0
  246. package/src/core/upgrade-run.js +59 -8
  247. package/src/dom/active-element.js +5 -0
  248. package/src/lib/assets.js +1 -0
  249. package/src/lib/autosave-scheduler.js +3 -4
  250. package/src/lib/slice.js +65 -0
  251. package/src/lib/uimotion.js +774 -0
  252. package/src/lib/unsaved-state.js +2 -2
  253. package/src/live/fast-lane.js +10 -3
  254. package/src/live/local-lane.js +202 -11
  255. package/src/live/ops.js +16 -0
  256. package/src/live/page-compaction.js +162 -0
  257. package/src/live/server-lane.js +1 -0
  258. package/src/live/store.js +11 -3
  259. package/src/loader-logic.js +3 -1
  260. package/src/loader.js +14 -4
  261. package/src/page/adopt-boot.js +2 -1
  262. package/src/page/awareness.js +33 -2
  263. package/src/page/blocks.js +122 -27
  264. package/src/page/board-views.js +2 -16
  265. package/src/page/boot-pack.js +1 -1
  266. package/src/page/chart-tables.js +29 -0
  267. package/src/page/chart.js +411 -72
  268. package/src/page/checklist.js +10 -135
  269. package/src/page/checkpoint-proof.js +295 -0
  270. package/src/page/chrome.js +257 -40
  271. package/src/page/clipboard-shape.js +126 -0
  272. package/src/page/document-title-dom.js +12 -0
  273. package/src/page/document-title-runtime.js +15 -1
  274. package/src/page/document-title.js +4 -1
  275. package/src/page/drag.js +33 -3
  276. package/src/page/editor.js +207 -31
  277. package/src/page/emoji-data.js +162 -0
  278. package/src/page/export-assets.js +357 -0
  279. package/src/page/export-html.js +209 -0
  280. package/src/page/export-markdown.js +361 -0
  281. package/src/page/features/board.js +12 -3
  282. package/src/page/features/chart.js +11 -0
  283. package/src/page/features/checklist.js +428 -0
  284. package/src/page/features/dates.js +180 -0
  285. package/src/page/features/emoji.js +187 -0
  286. package/src/page/features/export-html.js +189 -0
  287. package/src/page/features/export-markdown.js +169 -0
  288. package/src/page/features/import-markdown.js +349 -0
  289. package/src/page/features/media.js +61 -5
  290. package/src/page/features/mentions.js +188 -0
  291. package/src/page/features/registry.js +24 -1
  292. package/src/page/features/table.js +375 -85
  293. package/src/page/features/title-cover.js +331 -0
  294. package/src/page/features/title-icon.js +213 -0
  295. package/src/page/features/toc.js +87 -0
  296. package/src/page/format-command.js +33 -0
  297. package/src/page/format-gate.js +68 -18
  298. package/src/page/format-host.js +71 -0
  299. package/src/page/format-native-commands.js +158 -0
  300. package/src/page/format-native.js +42 -0
  301. package/src/page/image-actions.js +64 -0
  302. package/src/page/import-markdown.js +262 -0
  303. package/src/page/inline.js +32 -8
  304. package/src/page/items.js +141 -11
  305. package/src/page/markdown.js +30 -2
  306. package/src/page/media.js +35 -119
  307. package/src/page/mention-picker.js +99 -0
  308. package/src/page/merge.js +1 -1
  309. package/src/page/placeholder.js +14 -1
  310. package/src/page/projection.js +156 -25
  311. package/src/page/recover.js +10 -12
  312. package/src/page/recovery-ui.js +464 -0
  313. package/src/page/rich/chart.js +63 -7
  314. package/src/page/save-projection.js +67 -0
  315. package/src/page/save-states.js +14 -4
  316. package/src/page/schema.js +52 -3
  317. package/src/page/table-paste.js +3 -1
  318. package/src/page/table-style.js +4 -4
  319. package/src/page/table.js +44 -2
  320. package/src/page/themes/content.css +25 -1
  321. package/src/page/themes/light-1.css +33 -8
  322. package/src/page/themes/light-1.source.css +8 -7
  323. package/src/page/toc-link.js +31 -0
  324. package/src/page/toc.js +5 -0
  325. package/src/page/view-hooks.js +58 -0
  326. package/src/page/view.js +13 -0
  327. package/src/page/ybind/binding.js +116 -6
  328. package/src/page/ybind/bridge.js +187 -20
  329. package/src/page/ybind/containment.js +162 -0
  330. package/src/page/ybind/doc.js +17 -9
  331. package/src/page/ybind/file-debt.js +63 -0
  332. package/src/page/ybind/layout.js +7 -2
  333. package/src/page/ybind/marks.js +2 -1
  334. package/src/page/ybind/moves.js +5 -4
  335. package/src/page/ybind/origins.js +106 -4
  336. package/src/page/ybind/parts.js +8 -1
  337. package/src/page/ybind/provider.js +125 -18
  338. package/src/page/ybind/recovery-proof.js +32 -9
  339. package/src/page/ybind/recovery-replay.js +905 -4
  340. package/src/page/ybind/recovery.js +174 -45
  341. package/src/page/ybind/table.js +67 -11
  342. package/src/page/ybind/walk-delta.js +258 -0
  343. package/src/page/ybind/walk.js +54 -29
  344. package/src/page/ybind/writer.js +1 -1
  345. package/src/plugins/ai-edit.js +13 -9
  346. package/src/plugins/indicator-chip.js +91 -0
  347. package/src/plugins/indicator.js +13 -80
  348. package/src/plugins/live.js +126 -15
  349. package/src/plugins/menu/actions.js +196 -0
  350. package/src/plugins/menu/people.js +279 -0
  351. package/src/plugins/menu/placement.js +121 -0
  352. package/src/plugins/menu/preferences.js +9 -0
  353. package/src/plugins/menu/seo.js +316 -397
  354. package/src/plugins/menu/style.js +171 -0
  355. package/src/plugins/menu/variants.js +296 -0
  356. package/src/plugins/menu.js +186 -183
  357. package/src/plugins/page-view.js +3 -2
  358. package/src/plugins/page.js +362 -43
  359. package/src/plugins/sortable.js +8 -1
  360. package/src/plugins/source.js +6 -0
  361. package/src/plugins/upgrade.js +51 -17
  362. package/src/plugins/upload-ui.js +12 -17
  363. package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
  364. package/src/plugins/visitor-drafts/intro-style.js +7 -2
  365. package/src/plugins/visitor-drafts/intro.js +7 -4
  366. package/src/plugins/visitor-drafts/owner-chip.js +8 -12
  367. package/src/plugins/visitor-drafts/prompt.js +11 -9
  368. package/src/plugins/visitor-drafts/source.js +4 -3
  369. package/src/plugins/visitor-drafts/store.js +138 -9
  370. package/src/plugins/visitor-drafts/ui-memory.js +2 -2
  371. package/src/plugins/visitor-drafts-owner.js +4 -6
  372. package/src/plugins/visitor-drafts.js +5 -3
  373. package/src/recovery/store.js +1 -1
  374. package/src/route/route.js +4 -0
  375. package/src/server/checkpoint/index.js +2 -2
  376. package/src/server/rooms/index.js +5 -1
  377. package/src/server/rooms/live-data-rooms.js +302 -50
  378. package/src/server/rooms/page-compaction-worker.js +12 -0
  379. package/src/server/rooms/page-compactor.js +92 -0
  380. package/src/server/rooms/page-room-conformance.js +461 -0
  381. package/src/source/parse-steps.js +94 -0
  382. package/src/source/session.js +233 -57
  383. package/src/sync/editing-sender.js +12 -0
  384. package/src/sync/live-sync-edit.js +22 -2
  385. package/src/sync/live-sync.js +150 -23
  386. package/src/sync/page-relay.js +52 -0
  387. package/src/sync/presence-store.js +44 -4
  388. package/src/sync/presence.js +6 -3
  389. package/src/sync/saved-stream.js +28 -14
  390. package/src/ui/bevel-controls.js +1 -0
  391. package/src/ui/bevel-dialog.js +5 -2
  392. package/src/ui/editor-ui.js +77 -0
  393. package/src/ui/page-colour.js +281 -0
  394. package/src/ui/toast.js +16 -7
  395. package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
  396. package/src/vendor/hyper-undo.vendor.js +1 -1
  397. package/src/vendor/hypercms.vendor.js +9 -9
  398. package/src/vendor/parse5.vendor.js +2 -2
  399. package/src/vendor/sap-core.vendor.js +5 -5
  400. package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +0 -1
  401. package/dist/m/attrs/save-freeze-FUP4Q4X3.js +0 -1
  402. package/dist/m/c-2M73252O.js +0 -1
  403. package/dist/m/c-2SPVQSHC.js +0 -1
  404. package/dist/m/c-35NBGMZB.js +0 -1
  405. package/dist/m/c-3IQRECYS.js +0 -1
  406. package/dist/m/c-4B6H4G5V.js +0 -1
  407. package/dist/m/c-4FFHZM32.js +0 -1
  408. package/dist/m/c-4YSCRICO.js +0 -1
  409. package/dist/m/c-54RCDCBU.js +0 -1
  410. package/dist/m/c-5M4YEKCX.js +0 -4
  411. package/dist/m/c-5V3MOVJT.js +0 -1
  412. package/dist/m/c-66O32MV3.js +0 -28
  413. package/dist/m/c-6HMB24EV.js +0 -1
  414. package/dist/m/c-7O6AFR65.js +0 -1
  415. package/dist/m/c-ANQTJ3B5.js +0 -2
  416. package/dist/m/c-AYVOREBE.js +0 -1
  417. package/dist/m/c-B2ZXPOS3.js +0 -3
  418. package/dist/m/c-BMIZKMPV.js +0 -1
  419. package/dist/m/c-BTDI4Y23.js +0 -1
  420. package/dist/m/c-CJZSLMVQ.js +0 -1
  421. package/dist/m/c-DBE6ORSI.js +0 -1
  422. package/dist/m/c-EDHUR4MX.js +0 -1
  423. package/dist/m/c-EY6UOM54.js +0 -1
  424. package/dist/m/c-F3CKJTJS.js +0 -1
  425. package/dist/m/c-F7Y6FLPY.js +0 -1
  426. package/dist/m/c-FA4DZVTC.js +0 -1
  427. package/dist/m/c-G2BUZITR.js +0 -1
  428. package/dist/m/c-GB3JTV6E.js +0 -1
  429. package/dist/m/c-GBGWXTX4.js +0 -1
  430. package/dist/m/c-GHBCCVG4.js +0 -1
  431. package/dist/m/c-GJAEGGXY.js +0 -1
  432. package/dist/m/c-GOWBDM6J.js +0 -1
  433. package/dist/m/c-IB52R237.js +0 -1
  434. package/dist/m/c-IKWNE4R4.js +0 -1
  435. package/dist/m/c-IU3IYEU6.js +0 -1
  436. package/dist/m/c-IZ2EBRAJ.js +0 -1
  437. package/dist/m/c-KR4UW4KW.js +0 -1
  438. package/dist/m/c-KY3VJGUA.js +0 -8
  439. package/dist/m/c-LFPHCXY3.js +0 -1
  440. package/dist/m/c-LRPUO2MX.js +0 -1
  441. package/dist/m/c-LXENEBV7.js +0 -1
  442. package/dist/m/c-NMJRXKPC.js +0 -8
  443. package/dist/m/c-OIBUTDXI.js +0 -1
  444. package/dist/m/c-PPOSCAQD.js +0 -1
  445. package/dist/m/c-QIX5BJYN.js +0 -1
  446. package/dist/m/c-QQHEX6HA.js +0 -1
  447. package/dist/m/c-R7RMANJI.js +0 -9
  448. package/dist/m/c-RI4NT75Y.js +0 -1
  449. package/dist/m/c-RM7J7BV2.js +0 -1
  450. package/dist/m/c-RUJKSUQW.js +0 -1
  451. package/dist/m/c-SGCCC4FW.js +0 -25
  452. package/dist/m/c-T4VO4OCB.js +0 -1
  453. package/dist/m/c-TQEMVJCS.js +0 -1
  454. package/dist/m/c-TR6RMDQ7.js +0 -3
  455. package/dist/m/c-UKHTLWUW.js +0 -1
  456. package/dist/m/c-UNN7LWCT.js +0 -1
  457. package/dist/m/c-UXBMRDZB.js +0 -235
  458. package/dist/m/c-V7PZZRG7.js +0 -1
  459. package/dist/m/c-VL52ADDR.js +0 -1
  460. package/dist/m/c-WNYBLIOZ.js +0 -1
  461. package/dist/m/c-WWDBYXLK.js +0 -1
  462. package/dist/m/c-XNNW2CWK.js +0 -1
  463. package/dist/m/c-Y6USGTS5.js +0 -5
  464. package/dist/m/c-YG2NJYS6.js +0 -135
  465. package/dist/m/c-Z4VRC7GU.js +0 -7
  466. package/dist/m/c-Z6K52OLP.js +0 -19
  467. package/dist/m/c-ZDY2MDAA.js +0 -21
  468. package/dist/m/c-ZUCOKOHT.js +0 -1
  469. package/dist/m/core/admin-attrs-VGNLAUDK.js +0 -1
  470. package/dist/m/core/autosave-E4PEFPZ6.js +0 -1
  471. package/dist/m/core/conflict-notice-VQ6Y64PH.js +0 -1
  472. package/dist/m/core/host-permission-XN3K2IW3.js +0 -1
  473. package/dist/m/core/is-edit-mode-JXHDVGUL.js +0 -1
  474. package/dist/m/core/recovery-TBBSICE2.js +0 -1
  475. package/dist/m/core/save-WUCHTQBE.js +0 -1
  476. package/dist/m/core/save-core-FJVHT5M3.js +0 -1
  477. package/dist/m/core/snapshot-F3SWDZMM.js +0 -1
  478. package/dist/m/core/unsaved-warning-STKBHE6G.js +0 -1
  479. package/dist/m/events/index-23DPTUT4.js +0 -1
  480. package/dist/m/internals/index-WOVAP3RU.js +0 -1
  481. package/dist/m/lib/cache-bust-CGWT6ZJI.js +0 -1
  482. package/dist/m/loader-HXX6YNH5.js +0 -1
  483. package/dist/m/manifest-F22DEDAC.js +0 -1
  484. package/dist/m/manifest-edit-C3C7376B.js +0 -1
  485. package/dist/m/options/options-2LH76JQ3.js +0 -1
  486. package/dist/m/page/editor-XUTB2HB2.js +0 -189
  487. package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +0 -6
  488. package/dist/m/plugins/demo-ZIBFAA3R.js +0 -1
  489. package/dist/m/plugins/indicator-PV4YW2ID.js +0 -1
  490. package/dist/m/plugins/live-NUTTXSBS.js +0 -1
  491. package/dist/m/plugins/menu-HCKO3DSP.js +0 -138
  492. package/dist/m/plugins/page-KSUH7G6J.js +0 -2
  493. package/dist/m/plugins/page-view-W73OW7WI.js +0 -1
  494. package/dist/m/plugins/people-AJJ32YCG.js +0 -1
  495. package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
  496. package/dist/m/plugins/sortable-YRMQX3ZU.js +0 -1
  497. package/dist/m/plugins/source-KHK5UUSK.js +0 -1
  498. package/dist/m/plugins/undo-OF2L3CXU.js +0 -1
  499. package/dist/m/plugins/upgrade-ZNYODP5Y.js +0 -1
  500. package/dist/m/plugins/upload-XSGNILUY.js +0 -1
  501. package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +0 -21
  502. package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +0 -1
  503. package/dist/m/plugins/wire-ZWVWKF76.js +0 -1
  504. package/dist/m/reader-core-ILWLXVMP.js +0 -68
  505. package/dist/m/route/route-KOCZSEGN.js +0 -1
  506. package/dist/m/sync/live-sync-4AUVXVJU.js +0 -1
  507. package/dist/m/ui/index-EVHKMKBL.js +0 -1
  508. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
  509. /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
  510. /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
  511. /package/dist/m/plugins/{richclay-RC3L2F6X.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
@@ -11,6 +11,8 @@
11
11
  import { chartModel, renderChartSvg } from "./rich/chart.js";
12
12
  import { cellText } from "./rich/cell.js";
13
13
  import { bounded, descriptors, entriesOf, keyOf, normalizeEntries } from "./format-codec.js";
14
+ import { nativeAttrs } from "./format-native.js";
15
+ import { safeSrc } from "./address.js";
14
16
 
15
17
  export const ATOM = "";
16
18
  export const BREAK = '@{"type":"hard_break"}';
@@ -26,6 +28,13 @@ const KNOWN = new Set(CODES);
26
28
  export const OPAQUE = "data-clay-page-opaque";
27
29
  export const VALUE = "data-clay-page-value";
28
30
  export const MARKS = "data-clay-page-marks";
31
+ // A person mention's span states its id plainly beside the envelope that carries it:
32
+ // the label is the text, and a reader without the editor shows a name, never a link.
33
+ export const MENTION = "data-page-mention";
34
+ // A date mention reads as a `time` element: the text is the ISO date and `datetime`
35
+ // repeats it, so a reader without the editor shows the day and no zone or reminder.
36
+ export const DATE = "data-page-date";
37
+ const DAY = /^\d{4}-\d{2}-\d{2}$/;
29
38
  const ENVELOPE = "1";
30
39
  const STRUCTURAL = new Set(["id", "t", "parent", "c", "m"]);
31
40
  const knownType = (t) => Object.hasOwn(DEFAULTS, t);
@@ -33,12 +42,14 @@ const knownType = (t) => Object.hasOwn(DEFAULTS, t);
33
42
  // Mirrors schema.js attrs minus id and pos (a test pins the two together).
34
43
  export const DEFAULTS = {
35
44
  paragraph: { parent: null },
36
- heading: { parent: null, level: 2 },
45
+ heading: { parent: null, level: 2, pageIcon: null, pageCoverSrc: null, pageCoverAlt: "", pageCoverX: 50, pageCoverY: 50 },
37
46
  list_item: { parent: null, kind: "bullet", indent: 0, checked: false, owner: null, due: null },
38
47
  callout: { parent: null },
48
+ quote: { parent: null },
39
49
  code_block: { parent: null, lang: "" },
40
- toggle: { parent: null, open: false },
50
+ toggle: { parent: null, open: false, level: null },
41
51
  divider: { parent: null },
52
+ toc: { parent: null, maxLevel: null },
42
53
  image: { parent: null, src: "", alt: "", caption: "", width: null, align: null },
43
54
  file: { parent: null, src: "", name: "", size: null, mime: null },
44
55
  table: { parent: null, cols: {}, sort: null },
@@ -47,7 +58,44 @@ export const DEFAULTS = {
47
58
  tool: { parent: null, artifact: null, rev: null, state: {} },
48
59
  preserved: { parent: null, html: "" },
49
60
  };
50
- const TEXT = new Set(["paragraph", "heading", "list_item", "callout", "code_block", "toggle"]);
61
+ const TEXT = new Set(["paragraph", "heading", "list_item", "callout", "quote", "code_block", "toggle"]);
62
+ export const TOC_LABEL = "Table of contents";
63
+ export const TOC_ANCHOR = "page-block-";
64
+ export const blockAnchor = (id) => `${TOC_ANCHOR}${b64url(String(id ?? ""))}`;
65
+ const TOC_MAX_LEVEL = 3;
66
+ const tocLevel = (item) => (item.t === "heading"
67
+ ? Math.min(6, Math.max(1, Number.isInteger(item.level) ? item.level : 2))
68
+ : item.t === "toggle" && (item.level === 1 || item.level === 2 || item.level === 3) ? item.level : 0);
69
+ const tocTitle = (item) => item.t === "heading" && (item.level ?? 2) === 1 && (item.parent ?? null) === null;
70
+ const tocMax = (value) => (Number.isInteger(value) && value >= 1 && value <= 6 ? value : TOC_MAX_LEVEL);
71
+ const tocText = (item) => String(item.c ?? "").replace(/[\s\uFFFC]+/gu, " ").trim() || "Untitled";
72
+
73
+ export function tocEntries(model, maxLevel = TOC_MAX_LEVEL) {
74
+ const max = tocMax(maxLevel);
75
+ const title = model.find(tocTitle) ?? null;
76
+ const out = [];
77
+ for (const item of model) {
78
+ const level = tocLevel(item);
79
+ if (!item.id || !level || level > max || item === title) continue;
80
+ out.push({ id: item.id, level, text: tocText(item) });
81
+ }
82
+ return out;
83
+ }
84
+
85
+ export function tocHtml(item, model) {
86
+ const entries = tocEntries(model, item.maxLevel);
87
+ const root = { level: 0, children: [] };
88
+ const stack = [root];
89
+ for (const entry of entries) {
90
+ while (stack.length > 1 && stack[stack.length - 1].level >= entry.level) stack.pop();
91
+ const node = { ...entry, children: [] };
92
+ stack[stack.length - 1].children.push(node);
93
+ stack.push(node);
94
+ }
95
+ const list = (nodes) => `<ul>${nodes.map((node) => `<li><a href="#${blockAnchor(node.id)}">${escText(node.text)}</a>${node.children.length ? list(node.children) : ""}</li>`).join("")}</ul>`;
96
+ const level = item.maxLevel == null ? "" : ` data-max-level="${escAttr(item.maxLevel)}"`;
97
+ return `<nav aria-label="${TOC_LABEL}" data-type="toc" data-id="${escAttr(item.id ?? "")}"${level}>${entries.length ? list(root.children) : ""}</nav>`;
98
+ }
51
99
 
52
100
  const same = (a, b) => canon(a) === canon(b);
53
101
 
@@ -146,18 +194,25 @@ export function runsOf(units) {
146
194
  return out;
147
195
  }
148
196
 
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.
197
+ // A mark key as { marks: { b: true, a: ["href"] ... }, native: [...], entries: [...] }:
198
+ // the codes this runtime knows, the named descriptors of the four formats it
199
+ // writes (format-native.js), and the entries of every mark it has no code for,
200
+ // each in the codec's canonical order.
152
201
  function marksOfKey(key) {
153
- const marks = {};
154
- if (!key || key[0] === "@") return { marks, entries: [] };
202
+ const marks = {}, native = [];
203
+ if (!key || key[0] === "@") return { marks, native: [], entries: [] };
155
204
  const opaque = [];
156
205
  for (const desc of descriptors(key)) {
157
- if (desc.name === undefined && KNOWN.has(desc.code)) marks[desc.code] = desc.attrs ?? true;
206
+ if (desc.name === undefined) {
207
+ if (KNOWN.has(desc.code)) marks[desc.code] = desc.attrs ?? true;
208
+ else opaque.push(desc);
209
+ continue;
210
+ }
211
+ const attrs = nativeAttrs(desc.name, desc.attrs);
212
+ if (attrs) native.push({ name: desc.name, attrs });
158
213
  else opaque.push(desc);
159
214
  }
160
- return { marks, entries: opaque.length ? entriesOf(opaque) : [] };
215
+ return { marks, native, entries: opaque.length ? entriesOf(opaque) : [] };
161
216
  }
162
217
 
163
218
  // One unit's key, as the record keeps it: the codes this runtime knows, then
@@ -165,8 +220,8 @@ function marksOfKey(key) {
165
220
  const unitKey = (key, text, i) => {
166
221
  if (!key) return "";
167
222
  if (key[0] === "@") return key === BREAK && text[i] === ATOM ? key : "";
168
- const { marks, entries } = marksOfKey(key);
169
- return keyOfMarks(safeMarks(marks), entries);
223
+ const { marks, native, entries } = marksOfKey(key);
224
+ return keyOfMarks(safeMarks(marks), native.length ? [...entries, ...entriesOf(native)] : entries);
170
225
  };
171
226
 
172
227
  // A link to a script URL is never kept: it would run on the page's origin when a
@@ -277,6 +332,8 @@ const omitDefaults = (t, fields) => {
277
332
  return out;
278
333
  };
279
334
 
335
+ const focal = (v) => Math.min(100, Math.max(0, Number.isFinite(+v) && v !== null && v !== "" ? +v : 50));
336
+
280
337
  const columnWidth = (value) => (typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.round(value) : null);
281
338
 
282
339
  function cellModel(value) {
@@ -324,9 +381,12 @@ const recordOfItem = (item) => {
324
381
 
325
382
  // The fields of a known block the runtime has no schema attribute for. They are
326
383
  // merged back only when serialized, inside the inert payload.
384
+ // Fields the renderer writes as visible markup, which the reader rebuilds from it.
385
+ const VISIBLE = { table: new Set(["rows"]) };
386
+
327
387
  const extrasOfItem = (item) => {
328
388
  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;
389
+ for (const [k, v] of Object.entries(item)) if (!STRUCTURAL.has(k) && !VISIBLE[item.t]?.has(k) && !(knownType(item.t) && Object.hasOwn(DEFAULTS[item.t], k))) out[k] = v;
330
390
  return Object.keys(out).length ? envelopeOf(out) : "";
331
391
  };
332
392
 
@@ -355,7 +415,8 @@ function projectOne({ id, b, t, parent, indent }) {
355
415
  for (const k of Object.keys(extras)) delete attrs[k];
356
416
  const fields = { ...attrs, parent };
357
417
  if (t === "list_item") fields.indent = indent;
358
- if (t === "heading") fields.level = clampLevel(attrs.level ?? 2);
418
+ if (t === "heading") Object.assign(fields, { level: clampLevel(attrs.level ?? 2), pageCoverSrc: safeSrc(fields.pageCoverSrc ?? null), pageCoverX: focal(fields.pageCoverX), pageCoverY: focal(fields.pageCoverY) });
419
+ if (t === "toggle") fields.level = toggleLevel(attrs.level);
359
420
  if (t === "image" && fields.width != null) fields.width = widthOf(String(fields.width));
360
421
  if (t === "image") fields.align = alignOf(fields.align);
361
422
  if (t === "image" && typeof fields.caption === "string") fields.caption = normalizeText(fields.caption, null).c;
@@ -378,7 +439,23 @@ function projectOne({ id, b, t, parent, indent }) {
378
439
  return item;
379
440
  }
380
441
 
442
+ // A record a newer writer left in an older writer's envelope, for a block type this
443
+ // build knows: the same projection a peer's record gets, without the fields this
444
+ // type does not declare.
445
+ function knownRecordItem(id, record, parent) {
446
+ const { t } = record;
447
+ const malformed = new SyntaxError(`page: an envelope names the known block type ${t}`);
448
+ if (TEXT.has(t) ? record.c != null && typeof record.c !== "string" : record.c != null || record.m != null) throw malformed;
449
+ let item;
450
+ try { item = projectOne({ id, b: record, t, parent, indent: 0 }); }
451
+ catch { throw malformed; }
452
+ const fields = DEFAULTS[t];
453
+ return Object.fromEntries(Object.entries(item).filter(([k]) => k === "id" || k === "t" || STRUCTURAL.has(k) || k === "rows" || Object.hasOwn(fields, k)));
454
+ }
455
+
381
456
  const clampLevel = (l) => Math.min(6, Math.max(1, Number.isInteger(l) ? l : 2));
457
+ // A toggle heading is level 1 to 3; anything else is a plain toggle.
458
+ export const toggleLevel = (l) => (l === 1 || l === 2 || l === 3 ? l : null);
382
459
  const widthOf = (s) => (/^\d+$/.test(s) ? Number(s) : s);
383
460
  export const alignOf = (a) => (a === "left" || a === "right" ? a : null);
384
461
 
@@ -410,6 +487,10 @@ const escText = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>
410
487
  const escAttr = (s) => escText(String(s)).replace(/"/g, "&quot;");
411
488
  const attr = (name, value) => (value === null || value === undefined ? "" : ` ${name}="${escAttr(value)}"`);
412
489
 
490
+ // The element each of the four formats is read as in the saved article; the
491
+ // text color and the highlight carry their palette key as data-color.
492
+ const NATIVE_TAGS = { underline: "u", text_color: "span", highlight: "mark" };
493
+
413
494
  export function renderInline(c, m, marksEnvelopes = true) {
414
495
  const units = unitsOf(m, c.length) || [];
415
496
  let html = "";
@@ -425,13 +506,22 @@ export function renderInline(c, m, marksEnvelopes = true) {
425
506
  }
426
507
  let j = i + 1;
427
508
  while (j < c.length && (units[j] || "") === key && !(c[j] === ATOM && key[0] === "@")) j++;
428
- const { marks, entries } = marksOfKey(key);
509
+ const { marks, native, entries } = marksOfKey(key);
429
510
  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 });
511
+ for (const n of native) want.push({ sig: `native${n.name}${canon(n.attrs)}`, tag: NATIVE_TAGS[n.name], color: n.attrs.color ?? null });
512
+ const mention = entries.find((entry) => entry.name === "mention");
513
+ const date = entries.find((entry) => entry.name === "date");
514
+ const day = date && DAY.test(String(date.attrs.date)) ? String(date.attrs.date) : null;
515
+ if (entries.length && marksEnvelopes) want.push({ sig: `marks${canon(entries)}`, tag: day ? "time" : "span", entries, mention: mention?.attrs.id, day });
431
516
  let keep = 0;
432
517
  while (keep < open.length && keep < want.length && open[keep].sig === want[keep].sig) keep++;
433
518
  closeTo(keep);
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); }
519
+ for (const w of want.slice(keep)) {
520
+ if (w.entries) html += w.day ? `<time${attr("datetime", w.day)}${attr(DATE, "")}${marksEnvelope(w.entries)}>` : `<span${marksEnvelope(w.entries)}${attr(MENTION, w.mention)}>`;
521
+ else if (w.tag === "a") html += `<a${attr("href", w.href)}>`;
522
+ else html += w.color ? `<${w.tag}${attr("data-color", w.color)}>` : `<${w.tag}>`;
523
+ open.push(w);
524
+ }
435
525
  html += escText(c.slice(i, j));
436
526
  i = j;
437
527
  }
@@ -440,9 +530,13 @@ export function renderInline(c, m, marksEnvelopes = true) {
440
530
  }
441
531
 
442
532
  const inlineOf = (item) => renderInline(item.c ?? "", item.m);
533
+ const titleHtml = (item) => `<header${attr("data-id", item.id)} data-page-heading="1"${extrasOfItem(item)}>${item.pageCoverSrc ? `<img data-page-cover${attr("src", item.pageCoverSrc)}${attr("alt", item.pageCoverAlt ?? "")} style="object-position:${item.pageCoverX ?? 50}% ${item.pageCoverY ?? 50}%">` : ""}${item.pageIcon ? `<span data-page-icon aria-hidden="true">${escText(item.pageIcon)}</span>` : ""}<h1>${inlineOf(item)}</h1></header>`;
443
534
  const toolData = (item) => item.artifact || item.rev || (item.state && Object.keys(item.state).length)
444
535
  ? `<script type="application/json" data-tool>${canon({ artifact: item.artifact ?? null, rev: item.rev ?? null, state: item.state ?? {} }).replace(/</g, "\\u003c")}</script>`
445
536
  : "";
537
+ // A header names its column for every cell under it: the table id and the column
538
+ // id together, so a copied column keeps a name no other table shares.
539
+ const headerId = (tableId, colId) => `${tableId}-${colId}`;
446
540
  const cellHtml = (cell) => (typeof cell === "string" ? escText(cell) : cell && typeof cell === "object" ? renderInline(String(cell.c ?? ""), cell.m) : "");
447
541
  const dataAttrs = (obj) => Object.entries(obj).map(([k, v]) => attr(`data-${k}`, v)).join("");
448
542
 
@@ -454,6 +548,7 @@ function renderLeaf(item, clean, tables) {
454
548
  case "paragraph": return `<p${at}>${inlineOf(item)}</p>`;
455
549
  case "heading": { const h = `h${item.level ?? 2}`; return `<${h}${at}>${inlineOf(item)}</${h}>`; }
456
550
  case "callout": return `<aside${at} class="page-callout">${inlineOf(item)}</aside>`;
551
+ case "quote": return `<blockquote${at}>${inlineOf(item)}</blockquote>`;
457
552
  case "code_block": return `<pre${at}${attr("data-lang", item.lang || null)}><code>${escText(item.c ?? "")}</code></pre>`;
458
553
  case "divider": return `<hr${at}>`;
459
554
  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>`;
@@ -462,9 +557,12 @@ function renderLeaf(item, clean, tables) {
462
557
  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>`;
463
558
  }
464
559
  case "table": {
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("");
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");
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>`;
560
+ const head = item.cols.map((c) => {
561
+ const sorted = item.sort?.col === c.id ? (item.sort.dir === "desc" ? "descending" : "ascending") : null;
562
+ return `<th${attr("data-col", c.id)}${attr("id", headerId(item.id, c.id))}${attr("data-kind", c.kind ?? null)}${attr("data-width", c.width ?? null)}${attr("style", c.width ? `width:${c.width}px` : null)} scope="col"${attr("aria-sort", sorted)}>${escText(c.name)}</th>`;
563
+ }).join("");
564
+ const body = item.rows.map((r) => `<tr${attr("data-id", r.id)}>${item.cols.map((c) => `<td${attr("headers", headerId(item.id, c.id))}>${cellHtml(r.cells[c.id])}</td>`).join("")}</tr>`).join("\n");
565
+ return `<table${at} aria-label="Table"${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>`;
468
566
  }
469
567
  case "chart": {
470
568
  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 });
@@ -517,6 +615,7 @@ export function fragmentCleaner(doc) {
517
615
  export function renderModel(model, { fallbackOf = () => "", clean = RAW } = {}) {
518
616
  const lines = [];
519
617
  const tables = new Map(model.filter((item) => item.t === "table").map((item) => [item.id, item]));
618
+ const title = model.find(tocTitle) ?? null;
520
619
  const stack = [null];
521
620
  const lists = [];
522
621
  const add = (s) => { if (lists.length) lines[lines.length - 1] += s; else lines.push(s); };
@@ -539,11 +638,14 @@ export function renderModel(model, { fallbackOf = () => "", clean = RAW } = {})
539
638
  continue;
540
639
  }
541
640
  if (item.t === "toggle") {
542
- add(`<details${attr("data-id", item.id)}${extrasOfItem(item)}${item.open ? " open" : ""}><summary>${inlineOf(item)}</summary>`);
641
+ const h = toggleLevel(item.level);
642
+ add(`<details${attr("data-id", item.id)}${extrasOfItem(item)}${item.open ? " open" : ""}>${h ? `<summary><h${h}>${inlineOf(item)}</h${h}></summary>` : `<summary>${inlineOf(item)}</summary>`}`);
543
643
  stack.push(item.id);
544
644
  continue;
545
645
  }
546
646
  if (item.t === "tool") { add(`<section${attr("data-id", item.id)} data-type="tool">${toolData(item)}${clean.fallback(fallbackOf(item.id))}</section>`); continue; }
647
+ if (item.t === "toc") { add(tocHtml(item, model)); continue; }
648
+ if (item === title && (item.pageIcon || item.pageCoverSrc)) { add(titleHtml(item)); continue; }
547
649
  add(renderLeaf(item, clean, tables));
548
650
  }
549
651
  closeLists();
@@ -561,6 +663,17 @@ export function renderBlocks(blocks, opts) {
561
663
  const DROP_TAGS = new Set(["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"]);
562
664
  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"]);
563
665
 
666
+ // The named descriptor an inline element stands for, when it is one of the four
667
+ // formats: <u>, <mark> and a <span data-color> with a palette key. Anything else
668
+ // is not a format this runtime reads natively.
669
+ function nativeOfEl(tag, el) {
670
+ if (tag === "u") return { name: "underline", attrs: {} };
671
+ if (tag !== "mark" && tag !== "span") return null;
672
+ if (tag === "span" && !el.hasAttribute("data-color")) return null;
673
+ const name = tag === "mark" ? "highlight" : "text_color";
674
+ return { name, attrs: { color: el.getAttribute("data-color") } };
675
+ }
676
+
564
677
  // Inline content of el as { c, units }, minus the children skip(child) names.
565
678
  function inlineOfEl(el, skip = () => false) {
566
679
  let c = "";
@@ -579,7 +692,7 @@ function inlineOfEl(el, skip = () => false) {
579
692
  if (tag === "br") { c += ATOM; units.push(BREAK); continue; }
580
693
  if (tag === "span" && child.hasAttribute("data-atom")) { c += ATOM; units.push(child.getAttribute("data-atom")); continue; }
581
694
  const next = { ...marks };
582
- if (tag === "span" && child.hasAttribute(MARKS)) {
695
+ if ((tag === "span" || tag === "time") && child.hasAttribute(MARKS)) {
583
696
  walk(child, next, normalizeEntries([...entries, ...entriesOfEnvelope(child.getAttribute(MARKS))]));
584
697
  continue;
585
698
  }
@@ -588,7 +701,8 @@ function inlineOfEl(el, skip = () => false) {
588
701
  else if (tag === "s" || tag === "del" || tag === "strike") next.s = true;
589
702
  else if (tag === "code") next.c = true;
590
703
  else if (tag === "a" && child.hasAttribute("href")) next.a = [child.getAttribute("href")];
591
- walk(child, next, entries);
704
+ const named = nativeOfEl(tag, child);
705
+ walk(child, next, named ? [...entries, named] : entries);
592
706
  }
593
707
  };
594
708
  walk(el, {}, []);
@@ -663,7 +777,11 @@ export function parseArticle(article) {
663
777
  const P = { parent };
664
778
  const record = envelopeAt(el);
665
779
  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}`);
780
+ if (record.t === "opaque_block") throw new SyntaxError(`page: an envelope names the known block type ${record.t}`);
781
+ if (knownType(record.t)) {
782
+ items.push(knownRecordItem(idOf(el), record, parent));
783
+ return;
784
+ }
667
785
  const { t, ...fields } = record;
668
786
  items.push({ id: idOf(el), t, parent, ...fields });
669
787
  return;
@@ -671,17 +789,30 @@ export function parseArticle(article) {
671
789
  const onlyImage = tag === "P" && el.children.length === 1 && el.children[0].tagName === "IMG" && !el.textContent.trim();
672
790
  if (tag === "P" && !onlyImage) return push(el, "paragraph", parent, { ...omitDefaults("paragraph", P), ...tf(el) });
673
791
  if (/^H[1-6]$/.test(tag)) return push(el, "heading", parent, { ...omitDefaults("heading", { ...P, level: +tag[1] }), ...tf(el) });
792
+ if (tag === "HEADER" && el.getAttribute("data-page-heading") === ENVELOPE) {
793
+ const icon = el.querySelector(":scope > [data-page-icon]");
794
+ const cover = el.querySelector(":scope > img");
795
+ const [, x, y] = /object-position:\s*([-\d.]+)%\s+([-\d.]+)%/.exec(cover?.getAttribute("style") ?? "") ?? [];
796
+ const cover0 = cover ? { pageCoverSrc: safeSrc(cover.getAttribute("src")), pageCoverAlt: cover.getAttribute("alt") ?? "", pageCoverX: focal(x), pageCoverY: focal(y) } : {};
797
+ return push(el, "heading", parent, { ...omitDefaults("heading", { ...P, level: 1, pageIcon: icon ? icon.textContent : null, ...cover0 }), ...tf(el.querySelector(":scope > h1")) });
798
+ }
674
799
  if (tag === "ASIDE") return push(el, "callout", parent, { ...omitDefaults("callout", P), ...tf(el) });
800
+ if (tag === "BLOCKQUOTE") return push(el, "quote", parent, { ...omitDefaults("quote", P), ...tf(el) });
675
801
  if (tag === "PRE") {
676
802
  const code = el.querySelector(":scope > code");
677
803
  return push(el, "code_block", parent, { ...omitDefaults("code_block", { ...P, lang: el.getAttribute("data-lang") || "" }), c: (code || el).textContent });
678
804
  }
679
805
  if (tag === "HR") return push(el, "divider", parent, omitDefaults("divider", P));
806
+ if (tag === "NAV" && el.getAttribute("aria-label") === TOC_LABEL) {
807
+ const maxLevel = +(el.getAttribute("data-max-level") ?? 0);
808
+ return push(el, "toc", parent, omitDefaults("toc", { ...P, maxLevel: maxLevel >= 1 && maxLevel <= 6 ? maxLevel : null }));
809
+ }
680
810
  if (tag === "UL" || tag === "OL") return list(el, parent, 0);
681
811
  if (tag === "DETAILS") {
682
812
  const summary = el.querySelector(":scope > summary");
813
+ const head = summary?.querySelector(":scope > h1, :scope > h2, :scope > h3");
683
814
  const at = items.length;
684
- push(el, "toggle", parent, { ...omitDefaults("toggle", { ...P, open: el.hasAttribute("open") }), ...tf(summary) });
815
+ push(el, "toggle", parent, { ...omitDefaults("toggle", { ...P, open: el.hasAttribute("open"), level: head ? Number(head.tagName[1]) : null }), ...tf(summary) });
685
816
  const body = el.ownerDocument.createElement("div");
686
817
  for (const n of [...el.childNodes]) if (n !== summary) body.appendChild(n.cloneNode(true));
687
818
  walk(body, { ref: at });
@@ -1,10 +1,15 @@
1
1
  import toast, { toastPersistent, dismissPersistent } from "../ui/toast-lazy.js";
2
2
  import { state as pmState } from "../vendor/prosemirror.vendor.js";
3
+ import { mountEditorUI } from "../ui/editor-ui.js";
3
4
 
4
5
  export const NOTICE_TEXT = "Some text you typed was kept aside when its block moved or was removed.";
5
6
 
6
- const STYLE_ID = "page-recover-style";
7
+ // The control and its panel sit in one shadow root inside the page shell, so the page's
8
+ // CSS cannot restyle them. The page's --page-* tokens still reach them, and they keep
9
+ // the page's font and colour.
7
10
  const STYLES = `
11
+ :host { font: inherit !important; color: inherit !important; }
12
+ .page-recover-root { display: contents; }
8
13
  .page-recover { position: fixed; right: 16px; bottom: 16px; z-index: 20; min-height: 36px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--page-ink); background: var(--page-pop-bg); border: 1px solid var(--page-chrome-line-rest); border-radius: 999px; cursor: pointer; }
9
14
  .page-recover[hidden], .page-recover-panel[hidden] { display: none; }
10
15
  .page-recover-panel { position: fixed; right: 16px; bottom: 64px; z-index: 21; width: min(380px, calc(100vw - 32px)); max-height: 60vh; overflow-y: auto; color: var(--page-ink); background: var(--page-pop-bg); border: 1px solid var(--page-chrome-line-rest); border-radius: var(--page-chrome-radius); padding: 8px; box-sizing: border-box; }
@@ -33,13 +38,6 @@ const el = (doc, tag, className, attrs = {}) => {
33
38
  return node;
34
39
  };
35
40
 
36
- function ensureStyle(doc) {
37
- if (doc.getElementById(STYLE_ID)) return;
38
- const style = doc.createElement("style");
39
- style.id = STYLE_ID;
40
- style.textContent = STYLES;
41
- doc.head.append(style);
42
- }
43
41
 
44
42
  function previewOf(doc, record) {
45
43
  const out = el(doc, "div", "page-recover-text");
@@ -74,13 +72,14 @@ export function focusBlock(view, id) {
74
72
 
75
73
  export function createRecoverList({ binding, mount, ownTokens = [], onRestored = () => {}, onStale = () => {} }) {
76
74
  const doc = mount.ownerDocument;
77
- ensureStyle(doc);
78
75
  const own = new Set(ownTokens.filter(Boolean));
79
76
  const button = el(doc, "button", "page-recover", { type: "button", "aria-haspopup": "dialog", "aria-expanded": "false" });
80
77
  const panel = el(doc, "div", "page-recover-panel", { role: "dialog", "aria-label": "Recover changes" });
81
78
  button.hidden = true;
82
79
  panel.hidden = true;
83
- mount.append(button, panel);
80
+ const content = el(doc, "div", "page-recover-root");
81
+ content.append(button, panel);
82
+ const ui = mountEditorUI({ name: "page-recover", content, css: [STYLES], parent: mount });
84
83
 
85
84
  let snapshot = { recoverable: [], notices: [] };
86
85
  let target = null;
@@ -186,8 +185,7 @@ export function createRecoverList({ binding, mount, ownTokens = [], onRestored =
186
185
  destroy() {
187
186
  off();
188
187
  dismissPersistent(NOTICE_TEXT);
189
- button.remove();
190
- panel.remove();
188
+ ui.remove();
191
189
  },
192
190
  };
193
191
  }