@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
package/src/page/media.js CHANGED
@@ -3,25 +3,31 @@
3
3
  // Nothing reaches the record until the host has answered with a URL; then one
4
4
  // transaction inserts the block at the bookmark. A cancelled or failed upload
5
5
  // leaves no trace in the record.
6
- import { state, view as pmView } from "../vendor/prosemirror.vendor.js";
6
+ //
7
+ // The paste, the drop, the picker and the upload itself are here, so a file lands
8
+ // where it was dropped without waiting for anything. What an upload can then offer
9
+ // (retry, embedding, the alt text and width the chrome sets) is in the media view
10
+ // part, which loads with the first paste, drop, insert or upload.
11
+ import { state } from "../vendor/prosemirror.vendor.js";
7
12
  import { schema } from "./schema.js";
8
13
  import { outline } from "./blocks.js";
9
14
  import { newId } from "./model.js";
10
15
  import { asStep } from "./blocksel.js";
11
16
  import { announce } from "./announce.js";
12
17
  import { safeFileHref } from "./address.js";
13
- import { mediaViews } from "./media-views.js";
14
18
 
15
19
  const { Plugin, PluginKey } = state;
16
- const { Decoration, DecorationSet } = pmView;
17
20
  const N = schema.nodes;
18
21
 
19
22
  export const mediaKey = new PluginKey("page-media");
20
23
  export const EMBED_MAX = 1.5 * 1024 * 1024;
21
- export const RETRY = new Set(["timeout", "network", "error", "bad-response"]);
22
- const runtime = new WeakMap();
24
+ export const mediaRuntime = new WeakMap();
23
25
 
24
- const boundary = (doc, pos) => {
26
+ // The view part hands its upload actions back here when it installs, so the
27
+ // bookmark's own buttons reach them without this module importing it.
28
+ export const mediaParts = new WeakMap();
29
+
30
+ export const boundary = (doc, pos) => {
25
31
  const $p = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));
26
32
  return $p.depth === 0 ? $p.pos : $p.after(1);
27
33
  };
@@ -109,31 +115,30 @@ export function insertFiles(view, files, place = hostPlace(view.state, view.stat
109
115
  return true;
110
116
  }
111
117
 
112
- const live = (view, id) => mediaKey.getState(view.state).items.find((x) => x.id === id);
113
- const update = (view, id, patch) => view.dispatch(view.state.tr.setMeta(mediaKey, { update: { id, ...patch } }));
114
- const readDataUrl = (file) => new Promise((ok, no) => { const r = new FileReader(); r.onload = () => ok(r.result); r.onerror = () => no(r.error); r.readAsDataURL(file); });
115
- const attrsFor = (it, src) => (it.kind === "image" ? { src, alt: "", caption: "" } : { src, name: it.name, size: it.file.size ?? null, mime: it.file.type || null });
118
+ export const live = (view, id) => mediaKey.getState(view.state).items.find((x) => x.id === id);
119
+ export const update = (view, id, patch) => view.dispatch(view.state.tr.setMeta(mediaKey, { update: { id, ...patch } }));
120
+ export const attrsFor = (it, src) => (it.kind === "image" ? { src, alt: "", caption: "" } : { src, name: it.name, size: it.file.size ?? null, mime: it.file.type || null });
116
121
 
117
- function settle(it, view, tr) {
122
+ export function settle(it, view, tr) {
118
123
  if (it.preview) URL.revokeObjectURL?.(it.preview);
119
124
  if (tr) view.dispatch(tr);
120
125
  }
121
126
 
122
- async function run(view, it) {
123
- const rt = runtime.get(view);
127
+ export async function run(view, it) {
128
+ const rt = mediaRuntime.get(view);
124
129
  const abort = new AbortController();
125
130
  rt.aborts.set(it.id, abort);
126
131
  let last = -5;
127
132
  const onProgress = ({ percent }) => {
128
133
  if (percent - last < 5 && percent !== 100) return;
129
134
  last = percent;
130
- if (runtime.has(view) && live(view, it.id)) update(view, it.id, { percent });
135
+ if (mediaRuntime.has(view) && live(view, it.id)) update(view, it.id, { percent });
131
136
  };
132
137
  let res;
133
138
  try { res = await rt.upload(it.file, { onProgress, signal: abort.signal }); }
134
139
  catch { res = { code: "network", msg: "Upload failed" }; }
135
140
  rt.aborts.delete(it.id);
136
- if (!runtime.has(view) || !live(view, it.id)) return;
141
+ if (!mediaRuntime.has(view) || !live(view, it.id)) return;
137
142
  if (res?.ok) {
138
143
  const src = safeFileHref(res.uploads?.[0]?.url);
139
144
  if (src) {
@@ -159,46 +164,19 @@ async function run(view, it) {
159
164
  announce(view, `${it.name}: ${message}`);
160
165
  }
161
166
 
162
- export async function embedUpload(view, id) {
163
- const it = live(view, id);
164
- if (!it || it.state !== "embed-choice") return;
165
- const src = await readDataUrl(it.file);
166
- if (!runtime.has(view) || !live(view, id)) return;
167
- settle(it, view, placeTr(view.state, id, attrsFor(it, src)));
168
- announce(view, "Saved inside the document");
169
- }
170
-
171
- export function retryUpload(view, id) {
172
- const it = live(view, id);
173
- if (!it) return;
174
- update(view, id, { state: "uploading", percent: 0, code: null, message: null });
175
- run(view, live(view, id));
176
- }
177
-
178
- export function cancelUpload(view, id) {
179
- const rt = runtime.get(view);
180
- const abort = rt?.aborts.get(id);
181
- const it = live(view, id);
182
- if (it) settle(it, view, removeTr(view.state, id));
183
- rt?.aborts.delete(id);
184
- abort?.abort();
185
- }
186
-
187
167
  const htmlHasText = (html) => html.replace(/<(meta|style|img)\b[^>]*>/gi, "").replace(/<[^>]+>/g, "").trim() !== "";
188
168
 
189
169
  export function mediaPlugin({ upload, features = null }) {
190
- let editorView = null;
191
170
  // Only the bookmark widget waits for the view part: the upload and its block
192
171
  // are core, and the block draws through the schema until the part arrives.
193
172
  const showBookmark = (view) => {
194
- if (!features || features.has("media") || !runtime.has(view)) return;
173
+ if (!features || features.has("media") || !mediaRuntime.has(view)) return;
195
174
  features.ensure("media").catch(() => {});
196
175
  };
197
176
  return new Plugin({
198
177
  key: mediaKey,
199
178
  state: mediaState,
200
179
  view(view) {
201
- editorView = view;
202
180
  const doc = view.dom.ownerDocument;
203
181
  const input = doc.createElement("input");
204
182
  input.type = "file";
@@ -206,7 +184,7 @@ export function mediaPlugin({ upload, features = null }) {
206
184
  input.hidden = true;
207
185
  input.className = "page-file-input";
208
186
  view.dom.parentElement.append(input);
209
- runtime.set(view, { upload, aborts: new Map(), input });
187
+ mediaRuntime.set(view, { upload, aborts: new Map(), input });
210
188
  input.addEventListener("change", () => {
211
189
  const files = [...(input.files ?? [])];
212
190
  const picker = mediaKey.getState(view.state).picker;
@@ -230,18 +208,18 @@ export function mediaPlugin({ upload, features = null }) {
230
208
  const b = e.target.closest?.(".page-upload-btn");
231
209
  if (!b) return;
232
210
  e.preventDefault();
211
+ const part = mediaParts.get(view);
233
212
  const id = b.dataset.upload;
234
- if (b.dataset.act === "retry") retryUpload(view, id);
235
- else if (b.dataset.act === "embed") embedUpload(view, id);
236
- else cancelUpload(view, id);
213
+ if (b.dataset.act === "retry") part?.retryUpload(view, id);
214
+ else if (b.dataset.act === "embed") part?.embedUpload(view, id);
215
+ else part?.cancelUpload(view, id);
237
216
  };
238
217
  view.dom.addEventListener("click", onClick);
239
218
  return {
240
219
  destroy() {
241
220
  view.dom.removeEventListener("click", onClick);
242
- if (editorView === view) editorView = null;
243
- const rt = runtime.get(view);
244
- runtime.delete(view);
221
+ const rt = mediaRuntime.get(view);
222
+ mediaRuntime.delete(view);
245
223
  for (const it of mediaKey.getState(view.state).items) if (it.preview) URL.revokeObjectURL?.(it.preview);
246
224
  for (const a of rt.aborts.values()) a.abort();
247
225
  input.remove();
@@ -249,14 +227,6 @@ export function mediaPlugin({ upload, features = null }) {
249
227
  };
250
228
  },
251
229
  props: {
252
- decorations(st) {
253
- const { items } = mediaKey.getState(st);
254
- const render = editorView ? mediaViews.get(editorView) : null;
255
- if (!items.length || !render) return null;
256
- return DecorationSet.create(st.doc, items.map((it) => Decoration.widget(boundary(st.doc, it.pos), (view) => render(view.dom.ownerDocument, it), {
257
- side: -1000 + it.order, key: `${it.id}:${it.state}:${it.percent}`, stopEvent: () => true, ignoreSelection: true,
258
- })));
259
- },
260
230
  handlePaste(view, event) {
261
231
  const data = event.clipboardData;
262
232
  const files = [...(data?.files ?? [])];
@@ -287,72 +257,18 @@ export function mediaPlugin({ upload, features = null }) {
287
257
  });
288
258
  }
289
259
 
290
- export function pickFiles(view, accept, place = hostPlace(view.state, view.state.selection.$head.index(0)), remove = null) {
291
- const rt = runtime.get(view);
292
- if (!rt) return false;
293
- const picker = { place, remove: remove && { ...remove, text: view.state.doc.textBetween(remove.from, remove.to) } };
294
- view.dispatch(view.state.tr.setMeta(mediaKey, { picker }));
295
- rt.input.accept = accept;
296
- rt.input.click();
297
- return true;
298
- }
299
-
300
260
  const indexOfId = (doc, id) => {
301
261
  let found = -1;
302
262
  doc.forEach((n, _, i) => { if (n.attrs.id === id) found = i; });
303
263
  return found;
304
264
  };
305
265
 
306
- export function setImageAttrs(view, id, patch) {
307
- const st = view.state;
308
- const index = indexOfId(st.doc, id);
309
- if (index < 0) return false;
310
- let at = 0;
311
- for (let i = 0; i < index; i++) at += st.doc.child(i).nodeSize;
312
- const node = st.doc.child(index);
313
- view.dispatch(asStep(st.tr.setNodeMarkup(at, null, { ...node.attrs, ...patch })));
314
- return true;
315
- }
316
-
317
- export function openAltEditor(view, id) {
318
- const index = indexOfId(view.state.doc, id);
319
- if (index < 0) return false;
320
- const shell = view.dom.parentElement;
321
- const doc = shell.ownerDocument;
322
- let at = 0;
323
- for (let i = 0; i < index; i++) at += view.state.doc.child(i).nodeSize;
324
- const box = doc.createElement("div");
325
- box.className = "page-link";
326
- box.dataset.mode = "alt";
327
- const input = doc.createElement("input");
328
- input.className = "page-link-input";
329
- input.type = "text";
330
- input.value = view.state.doc.child(index).attrs.alt || "";
331
- input.placeholder = "Describe the image";
332
- input.setAttribute("aria-label", "Alt text");
333
- box.append(input);
334
- const rect = view.nodeDOM(at)?.getBoundingClientRect?.();
335
- const shellBox = shell.getBoundingClientRect();
336
- if (rect) {
337
- box.style.setProperty("--page-pop-x", `${rect.left - shellBox.left}px`);
338
- box.style.setProperty("--page-pop-y", `${rect.bottom - shellBox.top + 6}px`);
339
- }
340
- shell.append(box);
341
- let done = false;
342
- const close = (commit, refocus = true) => {
343
- if (done) return;
344
- done = true;
345
- const value = input.value.trim();
346
- box.remove();
347
- const i = indexOfId(view.state.doc, id);
348
- if (commit && i >= 0 && value !== view.state.doc.child(i).attrs.alt) setImageAttrs(view, id, { alt: value });
349
- if (refocus) view.focus();
350
- };
351
- input.addEventListener("keydown", (e) => {
352
- if (e.key === "Enter") { e.preventDefault(); close(true); }
353
- else if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); close(false); }
354
- });
355
- input.addEventListener("blur", () => close(true, false));
356
- input.focus();
266
+ export function pickFiles(view, accept, place = hostPlace(view.state, view.state.selection.$head.index(0)), remove = null) {
267
+ const rt = mediaRuntime.get(view);
268
+ if (!rt) return false;
269
+ const picker = { place, remove: remove && { ...remove, text: view.state.doc.textBetween(remove.from, remove.to) } };
270
+ view.dispatch(view.state.tr.setMeta(mediaKey, { picker }));
271
+ rt.input.accept = accept;
272
+ rt.input.click();
357
273
  return true;
358
274
  }
@@ -0,0 +1,99 @@
1
+ // The people picker the @ command draws: one listbox at the caret, outside the saved
2
+ // article and never in it. It is built from the editor's own menu markup, so it needs no
3
+ // stylesheet of its own, and it takes no focus: the caret and the typed query stay in the
4
+ // document, and the active row is announced through the editor's aria-activedescendant as
5
+ // the slash menu does. A name is only ever text.
6
+ export const PICKER = "page-mention-picker";
7
+ export const OPTION = "page-menu-item";
8
+ const EMPTY = "No people to mention";
9
+
10
+ let count = 0;
11
+
12
+ // The people a picker may offer: the ones the document already names, with the host's live
13
+ // list laid over them by id, so a renamed person is shown under the name the host answers
14
+ // with. Nothing is invented when the host has no list.
15
+ export function peopleFrom(saved = [], allowed = null) {
16
+ const byId = new Map();
17
+ for (const person of saved ?? []) if (person && typeof person.id === "string") byId.set(person.id, person);
18
+ for (const person of Array.isArray(allowed) ? allowed : []) if (person && typeof person.id === "string") byId.set(person.id, person);
19
+ return [...byId.values()];
20
+ }
21
+
22
+ export const filterPeople = (people, query) => {
23
+ const q = String(query ?? "").trim().toLowerCase();
24
+ if (!q) return people;
25
+ return people.filter((person) => String(person.name ?? "").toLowerCase().includes(q));
26
+ };
27
+
28
+ // The shell takes the words a command shows, so a date command reuses the same listbox:
29
+ // same placement, same rows, same keyboard, and its own name for what it lists.
30
+ export function createMentionPicker({ document: doc = globalThis.document, onPick = () => {}, label = "Mention a person", empty = EMPTY } = {}) {
31
+ const id = `page-mentions-${++count}`;
32
+ const el = doc.createElement("div");
33
+ el.className = `page-menu ${PICKER}`;
34
+ el.id = id;
35
+ el.setAttribute("role", "listbox");
36
+ el.setAttribute("aria-label", label);
37
+ el.hidden = true;
38
+ let people = [];
39
+ let active = 0;
40
+
41
+ const draw = () => {
42
+ if (!people.length) {
43
+ const none = doc.createElement("div");
44
+ none.className = "page-menu-title";
45
+ none.textContent = empty;
46
+ el.replaceChildren(none);
47
+ return;
48
+ }
49
+ el.replaceChildren(...people.map((person, i) => {
50
+ const row = doc.createElement("button");
51
+ row.type = "button";
52
+ row.className = `${OPTION}${i === active ? " is-active" : ""}`;
53
+ row.id = `${id}-${i}`;
54
+ row.dataset.index = String(i);
55
+ row.setAttribute("role", "option");
56
+ row.setAttribute("aria-selected", String(i === active));
57
+ row.textContent = String(person.name ?? "");
58
+ row.addEventListener("mousedown", (event) => event.preventDefault());
59
+ row.addEventListener("click", () => onPick(i));
60
+ return row;
61
+ }));
62
+ };
63
+
64
+ // The caret the list hangs from, in the page's own placement variables: the same ones
65
+ // the block menu writes, so the existing menu stylesheet paints and lifts it.
66
+ const place = (at) => {
67
+ el.style.setProperty("--page-pop-x", `${at.x}px`);
68
+ el.style.setProperty("--page-pop-y", `${at.y}px`);
69
+ };
70
+
71
+ return {
72
+ el,
73
+ show: (next, at = null) => {
74
+ people = next;
75
+ active = 0;
76
+ draw();
77
+ if (at) place(at);
78
+ el.hidden = false;
79
+ },
80
+ move: (step) => {
81
+ if (!people.length) return;
82
+ active = (active + step + people.length) % people.length;
83
+ draw();
84
+ },
85
+ activeId: () => (people.length ? `${id}-${active}` : null),
86
+ index: () => active,
87
+ personAt: (i) => people[i] ?? null,
88
+ hide: () => {
89
+ el.hidden = true;
90
+ people = [];
91
+ active = 0;
92
+ el.replaceChildren();
93
+ },
94
+ destroy() {
95
+ this.hide();
96
+ el.remove();
97
+ },
98
+ };
99
+ }
package/src/page/merge.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { canon, renderModel, parseModel, fragmentCleaner } from "./projection.js";
2
2
 
3
- const TEXT = new Set(["paragraph", "heading", "list_item", "callout", "code_block", "toggle"]);
3
+ const TEXT = new Set(["paragraph", "heading", "list_item", "callout", "quote", "code_block", "toggle"]);
4
4
  const same = (a, b) => canon(a) === canon(b);
5
5
 
6
6
  function inlineOf(item, doc) {
@@ -5,6 +5,7 @@ const { Decoration, DecorationSet } = pmView;
5
5
 
6
6
  export const PLACEHOLDER_TEXT = "Type / for blocks";
7
7
  export const TITLE_PLACEHOLDER = "Untitled";
8
+ export const CHECK_PLACEHOLDER = "To do";
8
9
 
9
10
  function titlePlaceholder(doc) {
10
11
  let done = false;
@@ -18,11 +19,23 @@ function titlePlaceholder(doc) {
18
19
  return decoration;
19
20
  }
20
21
 
22
+ // An empty check item keeps its tags, so the line it leaves is the whole click area;
23
+ // the words say where to click.
24
+ function checkPlaceholders(doc) {
25
+ const decorations = [];
26
+ doc.forEach((node, offset) => {
27
+ if (node.type !== schema.nodes.list_item || node.attrs.kind !== "check") return;
28
+ if (node.content.size || node.attrs.checked) return;
29
+ decorations.push(Decoration.node(offset, offset + node.nodeSize, { class: "page-check-placeholder", "data-placeholder": CHECK_PLACEHOLDER }));
30
+ });
31
+ return decorations;
32
+ }
33
+
21
34
  export function placeholderPlugin(text = PLACEHOLDER_TEXT) {
22
35
  return new state.Plugin({
23
36
  props: {
24
37
  decorations(st) {
25
- const decorations = [];
38
+ const decorations = checkPlaceholders(st.doc);
26
39
  const title = titlePlaceholder(st.doc);
27
40
  if (title) decorations.push(title);
28
41
  const { selection } = st;