@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
@@ -15,6 +15,18 @@ export function customTitle(root) {
15
15
  return false;
16
16
  }
17
17
 
18
+ // Mark this document's title as its author's own, the way the SEO editor's custom
19
+ // title does. Returns whether it wrote the marker.
20
+ export function markCustomTitle(root) {
21
+ const head = headOf(root);
22
+ if (!head || customTitle(root)) return false;
23
+ const meta = head.ownerDocument.createElement("meta");
24
+ meta.setAttribute("name", TITLE_MODE);
25
+ meta.setAttribute("content", "custom");
26
+ head.insertBefore(meta, head.querySelector("title") ?? head.firstChild);
27
+ return true;
28
+ }
29
+
18
30
  export function titleText(root) {
19
31
  return headOf(root)?.querySelector("title")?.textContent ?? "";
20
32
  }
@@ -1,6 +1,7 @@
1
1
  import { state } from "../vendor/prosemirror.vendor.js";
2
2
  import Mutation from "../lib/mutation.js";
3
- import { titleFor, titleText, writeTitle } from "./document-title-dom.js";
3
+ import { UNTITLED } from "./document-title.js";
4
+ import { customTitle, markCustomTitle, titleFor, titleText, writeTitle } from "./document-title-dom.js";
4
5
 
5
6
  const ATOM = "\uFFFC";
6
7
 
@@ -38,6 +39,18 @@ export function refreshDocumentTitle(view) {
38
39
  return title;
39
40
  }
40
41
 
42
+ // A file written before titles followed the first H1 can carry a title its author
43
+ // typed with no H1 behind it. The first time an editor opens such a file, that title
44
+ // becomes the author's own (the SEO custom marker), so deriving never replaces it;
45
+ // a file that has an H1 keeps following it, and deleting that H1 later reads Untitled.
46
+ export function adoptAuthoredTitle(view) {
47
+ const doc = view?.dom?.ownerDocument ?? null;
48
+ if (!doc || customTitle(doc) || titleItemsOfDoc(view.state.doc).length || [ "", UNTITLED ].includes(titleText(doc).trim())) return false;
49
+ Mutation.pause();
50
+ try { return markCustomTitle(doc); }
51
+ finally { Mutation.resume(); }
52
+ }
53
+
41
54
  export function titlePlugin() {
42
55
  return new state.Plugin({
43
56
  view(editorView) {
@@ -46,6 +59,7 @@ export function titlePlugin() {
46
59
  doc = editorView.state.doc;
47
60
  refreshDocumentTitle(editorView);
48
61
  };
62
+ adoptAuthoredTitle(editorView);
49
63
  refresh();
50
64
  return {
51
65
  update(next) {
@@ -1,9 +1,12 @@
1
1
  export const UNTITLED = "Untitled";
2
2
 
3
- export function documentTitle(items, { custom = false, currentTitle = "" } = {}) {
3
+ export function documentTitle(items, { custom = false, currentTitle = "", keepWithoutHeading = false } = {}) {
4
4
  if (custom) return currentTitle;
5
5
  const first = items.find(item =>
6
6
  item.t === "heading" && (item.level ?? 2) === 1 && item.parent == null
7
7
  );
8
+ // A file the editor has not adopted yet (see adoptAuthoredTitle) may carry a title
9
+ // its author wrote with no H1 behind it; a renderer that cannot ask keeps it.
10
+ if (!first && keepWithoutHeading && !["", UNTITLED].includes(String(currentTitle).trim())) return currentTitle;
8
11
  return String(first?.c ?? "").replace(/[\s\uFFFC]+/gu, " ").trim() || UNTITLED;
9
12
  }
package/src/page/drag.js CHANGED
@@ -5,6 +5,8 @@ import { gapTargets, summaryTarget, pickTarget, moveSpanTo } from "./drop.js";
5
5
  import { announce } from "./announce.js";
6
6
 
7
7
  const SLOP = 4;
8
+ const HOLD = 250;
9
+ const HOLD_SLOP = 8;
8
10
  const EDGE = 56;
9
11
  const MAX_SPEED = 18;
10
12
 
@@ -70,6 +72,9 @@ export class BlockDrag {
70
72
  this.cancel();
71
73
  }
72
74
  };
75
+ this.onOther = (e) => { if (this.state && e.pointerId !== this.state.pointer) this.cancel(); };
76
+ this.onBlur = () => this.cancel();
77
+ this.onScroll = () => { if (this.state && !this.state.active) this.stop(); };
73
78
  grip.addEventListener("pointerdown", (e) => this.start(e));
74
79
  }
75
80
 
@@ -82,24 +87,43 @@ export class BlockDrag {
82
87
  this.state = {
83
88
  index, span: captureSpan(ents, lo, hi), touch: event.pointerType === "touch",
84
89
  pointer: event.pointerId, x0: event.clientX, y0: event.clientY, x: event.clientX, y: event.clientY,
85
- active: false, raf: 0, gap: undefined, targets: [], target: null,
90
+ hold: 0, active: false, raf: 0, gap: undefined, targets: [], target: null,
86
91
  };
87
92
  this.grip.setPointerCapture?.(event.pointerId);
88
93
  this.grip.addEventListener("pointermove", this.onMove);
89
94
  this.grip.addEventListener("pointerup", this.onUp);
90
95
  this.grip.addEventListener("pointercancel", this.onCancel);
91
96
  this.grip.addEventListener("lostpointercapture", this.onCancel);
97
+ if (this.state.touch) {
98
+ const s = this.state;
99
+ s.hold = setTimeout(() => this.held(s), HOLD);
100
+ window.addEventListener("pointerdown", this.onOther, true);
101
+ window.addEventListener("blur", this.onBlur);
102
+ window.addEventListener("scroll", this.onScroll, true);
103
+ }
92
104
  event.preventDefault();
93
105
  }
94
106
 
107
+ // A touch keeps still for a quarter second before it drags: the press the mouse
108
+ // starts on its first move only reaches the same place once a scroll would have
109
+ // begun, so the hold tells the two apart. A tap before then is the menu.
110
+ held(s) {
111
+ if (this.state !== s || s.active) return;
112
+ s.hold = 0;
113
+ if (Math.hypot(s.x - s.x0, s.y - s.y0) > HOLD_SLOP || !this.view.editable) return this.stop();
114
+ this.begin();
115
+ }
116
+
95
117
  move(event) {
96
118
  const s = this.state;
97
119
  if (!s || event.pointerId !== s.pointer) return;
98
120
  s.x = event.clientX;
99
121
  s.y = event.clientY;
122
+ if (s.touch && !this.view.editable) return s.active ? this.cancel() : this.stop();
100
123
  if (!s.active) {
101
- if (Math.hypot(s.x - s.x0, s.y - s.y0) <= SLOP) return;
102
- if (s.touch) return this.stop();
124
+ const reach = Math.hypot(s.x - s.x0, s.y - s.y0);
125
+ if (s.touch) { if (reach > HOLD_SLOP) this.stop(); return; }
126
+ if (reach <= SLOP) return;
103
127
  this.begin();
104
128
  }
105
129
  this.track();
@@ -167,6 +191,7 @@ export class BlockDrag {
167
191
  const step = () => {
168
192
  const s = this.state;
169
193
  if (!s?.active) return;
194
+ if (s.touch && !this.view.editable) return this.cancel();
170
195
  const sc = this.scroller;
171
196
  const box = sc === sc.ownerDocument.scrollingElement ? { top: 0, bottom: window.innerHeight } : sc.getBoundingClientRect();
172
197
  let v = 0;
@@ -204,6 +229,7 @@ export class BlockDrag {
204
229
  && (ents[lo].node.type.name !== "list_item" || target.indent === ents[lo].node.attrs.indent);
205
230
  if (same) return;
206
231
  this.view.dispatch(asStep(moveSpanTo(st, ents, lo, hi, target)).scrollIntoView());
232
+ if (this.view.state.doc === st.doc) return announce(this.view, "Move cancelled");
207
233
  const n = hi - lo + 1;
208
234
  announce(this.view, `Moved ${n} ${n === 1 ? "block" : "blocks"}`);
209
235
  this.view.focus();
@@ -218,12 +244,16 @@ export class BlockDrag {
218
244
  const s = this.state;
219
245
  if (!s) return;
220
246
  this.state = null;
247
+ clearTimeout(s.hold);
221
248
  cancelAnimationFrame(s.raf);
222
249
  this.grip.removeEventListener("pointermove", this.onMove);
223
250
  this.grip.removeEventListener("pointerup", this.onUp);
224
251
  this.grip.removeEventListener("pointercancel", this.onCancel);
225
252
  this.grip.removeEventListener("lostpointercapture", this.onCancel);
226
253
  this.grip.releasePointerCapture?.(s.pointer);
254
+ window.removeEventListener("pointerdown", this.onOther, true);
255
+ window.removeEventListener("blur", this.onBlur);
256
+ window.removeEventListener("scroll", this.onScroll, true);
227
257
  window.removeEventListener("keydown", this.onKey, true);
228
258
  this.shell.classList.remove("is-dragging");
229
259
  delete this.shell.dataset.drop;
@@ -15,20 +15,20 @@ import { announcementPlugin } from "./announce.js";
15
15
  import { linkPlugin } from "./link.js";
16
16
  import { blockSelectionPlugin, asStep } from "./blocksel.js";
17
17
  import { markdownPlugin, markdownKeys, backspaceToParagraph, composing } from "./markdown.js";
18
+ import { markable } from "./format-native.js";
18
19
  import { placeholderPlugin } from "./placeholder.js";
19
20
  import { titlePlugin } from "./document-title-runtime.js";
20
21
  import { mediaPlugin } from "./media.js";
21
22
  import { highlightSlot } from "./highlight-slot.js";
22
23
  import { tableKeys, tablePlugin } from "./table.js";
23
24
  import { tableSelectPlugin } from "./table-select.js";
24
- import { chartPlugin, ChartView } from "./chart.js";
25
- import { boardPlugin } from "./board-views.js";
26
- import { checklistPlugin, checklistKeys } from "./checklist.js";
25
+ import { viewFeatures, viewHookPlugin } from "./view-hooks.js";
26
+ import { checklistKeys } from "./checklist.js";
27
27
  import { csvImportPlugin } from "./csv-import.js";
28
28
  import { styleWithLegacyId } from "../lib/style.js";
29
29
  import { historyIntentPlugin } from "./history-intent.js";
30
30
  import { createFeatureLoader } from "./feature-loader.js";
31
- import { COMMAND_FEATURES, FEATURES, featureForType } from "./features/registry.js";
31
+ import { COMMAND_FEATURES, FEATURES, featureForNode } from "./features/registry.js";
32
32
  import { toolViews } from "./tool-views.js";
33
33
 
34
34
  const STYLE_ID = "page-editor-style";
@@ -39,6 +39,11 @@ const LIGHT = `
39
39
  --page-rule: rgba(35, 34, 31, 0.10); --page-accent: #2F5E9E; --page-link: #164583; --page-selection: rgba(88, 120, 170, 0.20);
40
40
  --page-check-border: rgba(35, 34, 31, 0.35); --page-check-fill: #23221F; --page-check-ink: #F7F6F2;
41
41
  --page-callout-bg: rgba(35, 34, 31, 0.04); --page-code-bg: rgba(35, 34, 31, 0.05);
42
+ --page-color-gray: #5B5953; --page-color-brown: #8A5A1E; --page-color-orange: #B4531A; --page-color-yellow: #7A6210; --page-color-green: #3E6A28;
43
+ --page-color-blue: #1F5A8A; --page-color-purple: #5E4596; --page-color-pink: #A23E6E; --page-color-red: #9E3B2F;
44
+ --page-mark-bg-gray: rgba(35, 34, 31, 0.12); --page-mark-bg-brown: rgba(138, 90, 30, 0.20); --page-mark-bg-orange: rgba(180, 83, 26, 0.20);
45
+ --page-mark-bg-yellow: rgba(122, 98, 16, 0.22); --page-mark-bg-green: rgba(62, 106, 40, 0.20); --page-mark-bg-blue: rgba(31, 90, 138, 0.18);
46
+ --page-mark-bg-purple: rgba(94, 69, 150, 0.18); --page-mark-bg-pink: rgba(162, 62, 110, 0.18); --page-mark-bg-red: rgba(158, 59, 47, 0.18);
42
47
  --page-code-token-keyword: #8A3B6E; --page-code-token-constant: #1F5A8A; --page-code-token-string: #3E6A28; --page-code-token-string-expression: #3E6A28; --page-code-token-comment: #8B877E; --page-code-token-function: #5E4596; --page-code-token-parameter: #8A5A1E; --page-code-token-punctuation: var(--page-ink-soft); --page-code-token-link: var(--page-link);
43
48
  --page-chrome-ink-rest: rgba(35, 34, 31, 0.20); --page-chrome-ink-moved: rgba(35, 34, 31, 0.34);
44
49
  --page-chrome-bg-rest: rgba(35, 34, 31, 0.02); --page-chrome-bg-moved: rgba(35, 34, 31, 0.04);
@@ -53,6 +58,11 @@ const DARK = `
53
58
  --page-rule: rgba(231, 228, 220, 0.10); --page-accent: #8DB0E6; --page-link: #8DB0E6; --page-selection: rgba(120, 155, 210, 0.28);
54
59
  --page-check-border: rgba(231, 228, 220, 0.35); --page-check-fill: #E7E4DC; --page-check-ink: #1A1A18;
55
60
  --page-callout-bg: rgba(231, 228, 220, 0.05); --page-code-bg: rgba(231, 228, 220, 0.06);
61
+ --page-color-gray: #A3A098; --page-color-brown: #E0B47A; --page-color-orange: #E8A177; --page-color-yellow: #DEC97A; --page-color-green: #A9C98B;
62
+ --page-color-blue: #8DB0E6; --page-color-purple: #BBA6E6; --page-color-pink: #E3A3C4; --page-color-red: #E08A7A;
63
+ --page-mark-bg-gray: rgba(231, 228, 220, 0.14); --page-mark-bg-brown: rgba(224, 180, 122, 0.22); --page-mark-bg-orange: rgba(232, 161, 119, 0.22);
64
+ --page-mark-bg-yellow: rgba(222, 201, 122, 0.24); --page-mark-bg-green: rgba(169, 201, 139, 0.22); --page-mark-bg-blue: rgba(141, 176, 230, 0.22);
65
+ --page-mark-bg-purple: rgba(187, 166, 230, 0.22); --page-mark-bg-pink: rgba(227, 163, 196, 0.22); --page-mark-bg-red: rgba(224, 138, 122, 0.22);
56
66
  --page-code-token-keyword: #D9A0C8; --page-code-token-constant: #8DB0E6; --page-code-token-string: #A9C98B; --page-code-token-string-expression: #A9C98B; --page-code-token-comment: #75726B; --page-code-token-function: #BBA6E6; --page-code-token-parameter: #E0B47A; --page-code-token-punctuation: var(--page-ink-soft); --page-code-token-link: var(--page-link);
57
67
  --page-chrome-ink-rest: rgba(231, 228, 220, 0.18); --page-chrome-ink-moved: rgba(231, 228, 220, 0.30);
58
68
  --page-chrome-bg-rest: rgba(231, 228, 220, 0.02); --page-chrome-bg-moved: rgba(231, 228, 220, 0.04);
@@ -100,6 +110,9 @@ export const PAGE_EDITOR_CSS = `
100
110
  .page-editor pre:hover .page-code-lang, .page-editor pre:focus-within .page-code-lang, .page-code-lang:focus-visible, .page-editor pre[data-lang] .page-code-lang { opacity: 1; }
101
111
  .page-code-lang:hover, .page-code-lang:focus-visible { background: var(--page-pop-hover); color: var(--page-ink-soft); }
102
112
  :where(article[data-page], .page-editor) .page-callout { background: var(--page-callout-bg); border: 1px solid var(--page-callout-line); border-radius: 8px; padding: 10px 14px; }
113
+ /* The four floor-1 formats. A quote is set apart by its own inset and ink, with
114
+ no left-border accent; the palette's keys are fixed tokens, in both themes. */
115
+ :where(article[data-page], .page-editor) blockquote { padding-inline-start: var(--page-indent); color: var(--page-ink-soft); }
103
116
  :where(article[data-page], .page-editor) hr { border: 0; border-top: 1px solid var(--page-rule); margin-block: 1.2em; }
104
117
  :where(article[data-page], .page-editor) .page-hidden { display: none; }
105
118
  :where(article[data-page], .page-editor) .page-li { position: relative; --li-indent: 0; padding-inline-start: calc((var(--li-indent) + 1) * var(--page-indent)); }
@@ -126,8 +139,8 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
126
139
  .page-tool { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 6px; border: 0; border-radius: 5px; background: none; color: inherit; font: 500 13px/1 var(--page-font); cursor: pointer; }
127
140
  .page-tool:hover, .page-tool[aria-pressed="true"] { background: var(--page-pop-hover); }
128
141
  .page-tool-turn { gap: 4px; padding: 0 8px; }
129
- /* The phone's fourth control: everything the narrow bar folds away. */
130
- .page-tool-more { display: none; }
142
+ /* The phone's fourth control folds away everything the narrow bar hides; on a desktop it
143
+ holds what has no button of its own (colors, icon and cover, the exports). */
131
144
  .page-tool-sep { width: 1px; height: 16px; margin: 0 3px; background: currentColor; opacity: 0.5; }
132
145
  .page-safe-probe { position: fixed; left: 0; bottom: 0; width: 0; height: 0; padding-bottom: env(safe-area-inset-bottom); visibility: hidden; pointer-events: none; }
133
146
  .page-plus { position: absolute; z-index: 2; top: calc(var(--page-plus-top, 0px) - var(--page-plus-size) / 2); left: calc(var(--page-plus-left, 0px) - var(--page-plus-size) - 12px); width: var(--page-plus-size); height: var(--page-plus-size); display: grid; place-items: center; padding: 0; cursor: pointer; }
@@ -137,10 +150,23 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
137
150
  .page-bubble, .page-menu { position: absolute; z-index: 4; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); box-shadow: var(--page-pop-shadow); border-radius: var(--page-pop-radius); }
138
151
  .page-bubble { display: flex; gap: 1px; padding: 3px; left: var(--page-pop-x, 0px); top: var(--page-pop-y, 0px); transform: translate(-50%, calc(-100% - 8px)); }
139
152
  .page-menu { left: var(--page-pop-x, 0px); top: var(--page-pop-y, 0px); width: 260px; max-height: 340px; overflow-y: auto; padding: 6px; font-size: 14px; }
153
+ .page-settings { position: absolute; z-index: 4; box-sizing: border-box; left: var(--page-pop-x, 0px); top: var(--page-pop-y, 0px); width: min(264px, calc(100vw - 28px)); padding: 9px 11px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); border-radius: var(--page-pop-radius); box-shadow: var(--page-pop-shadow); font-size: 13px; }
154
+ .page-settings-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-bottom: 6px; margin-bottom: 2px; border-bottom: 1px solid var(--page-pop-line); }
155
+ .page-settings-title { font-size: 12.5px; font-weight: 600; color: var(--page-pop-ink); }
156
+ .page-settings-close { display: inline-grid; place-content: center; width: 20px; height: 20px; padding: 0; border: 1px solid transparent; border-radius: 5px; background: none; color: var(--page-pop-muted); cursor: pointer; }
157
+ .page-settings-close:hover { background: var(--page-pop-hover); color: var(--page-pop-ink); }
158
+ .page-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 0; }
159
+ .page-settings-name { min-width: 0; font-weight: 500; line-height: 1.3; }
160
+ .page-settings-switch { position: relative; flex: 0 0 30px; box-sizing: border-box; width: 30px; height: 18px; padding: 0; border: 0; border-radius: 9px; background: var(--page-chrome-line-moved); cursor: pointer; transition: background-color var(--page-fade); }
161
+ .page-settings-switch[aria-checked="true"] { background: var(--page-accent); }
162
+ .page-settings-knob { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: var(--page-pop-bg); transition: transform var(--page-fade); }
163
+ .page-settings-switch[aria-checked="true"] .page-settings-knob { transform: translateX(12px); }
164
+ .page-settings-note { margin: 4px 0 2px; color: var(--page-pop-muted); font-size: 11px; line-height: 1.4; }
140
165
  .page-menu-title { padding: 4px 10px 6px; color: var(--page-pop-muted); font-size: 12px; }
141
166
  .page-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 34px; padding: 4px 10px; border: 0; border-radius: 7px; background: none; color: inherit; font: inherit; text-align: start; cursor: pointer; }
142
167
  .page-menu-item[aria-selected="true"], .page-menu-item.is-active, .page-menu-item:hover { background: var(--page-pop-hover); }
143
168
  .page-menu-icon { display: grid; place-items: center; width: 24px; height: 24px; border: 1px solid var(--page-pop-line); border-radius: 6px; color: var(--page-pop-muted); font-size: 12px; flex: none; }
169
+ .page-menu-icon svg { display: block; }
144
170
  .page-menu-hint { margin-inline-start: auto; color: var(--page-pop-muted); font-size: 12px; }
145
171
  .page-menu-text { display: flex; flex-direction: column; min-width: 0; }
146
172
  .page-menu-desc { color: var(--page-pop-muted); font-size: 12px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -164,11 +190,48 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
164
190
  .page-editor .page-title-placeholder::before { content: attr(data-placeholder); float: left; height: 0; color: var(--page-ink-faint); pointer-events: none; }
165
191
  .page-csv-error { margin: 8px 0; color: var(--page-danger); font-size: 14px; }
166
192
  .page-editor .page-table { max-width: 100%; min-width: 0; border: 1px solid var(--page-rule); border-radius: 8px; }
167
- .page-editor .page-chart { position: relative; max-width: 100%; min-width: 0; margin-inline: 0; color: var(--page-ink); }
193
+ .page-editor .page-chart { position: relative; max-width: 100%; min-width: 0; margin-inline: 0; border: 1px solid var(--page-rule); border-radius: var(--page-chrome-radius); color: var(--page-ink); }
168
194
  .page-chart-plot svg { display: block; width: 100%; height: auto; color: var(--chart-text, var(--page-ink-soft)); font: 12px var(--page-font); }
169
- .page-chart figcaption { color: var(--page-ink-soft); font-size: 0.88em; text-align: center; }
170
- .page-chart-settings { display: block; margin: 8px auto 0; padding: 4px 10px; border: 1px solid var(--page-rule); border-radius: 6px; background: var(--page-pop-bg); color: var(--page-ink); font: inherit; cursor: pointer; }
171
- .page-chart-title-input { display: block; width: min(100%, 24em); box-sizing: border-box; margin: 6px auto; padding: 4px 8px; border: 1px solid var(--page-rule); border-radius: 6px; background: var(--page-pop-bg); color: var(--page-ink); font: inherit; }
195
+ .page-chart-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--page-rule); }
196
+ .page-chart-title { min-width: 0; margin-inline-end: auto; font-size: 14px; font-weight: 600; line-height: 1.3; }
197
+ .page-chart-title.is-empty { color: var(--page-ink-faint); font-weight: 500; }
198
+ .page-chart-source { display: inline-flex; align-items: center; gap: 5px; box-sizing: border-box; height: 22px; padding: 0 7px; border: 1px solid var(--page-pop-line); border-radius: var(--page-chrome-radius); background: var(--page-pop-bg); color: var(--page-pop-muted); font: inherit; font-size: 11.5px; cursor: pointer; }
199
+ .page-chart-source:hover { color: var(--page-ink); border-color: var(--page-chrome-line-moved); }
200
+ .page-chart-source svg, .page-chart-edit svg, .page-chart-remove svg { flex: none; }
201
+ .page-chart-edit { display: inline-flex; align-items: center; gap: 6px; box-sizing: border-box; height: 28px; padding: 0 10px; border: 1px solid var(--page-rule); border-radius: var(--page-chrome-radius); background: none; color: var(--page-ink); font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
202
+ .page-chart-edit:hover, .page-chart-edit[aria-expanded="true"] { background: var(--page-pop-hover); }
203
+ .page-chart-panel { position: absolute; z-index: 5; top: 46px; inset-inline-end: 10px; box-sizing: border-box; width: 304px; padding: 12px; border: 1px solid var(--page-pop-line); border-radius: var(--page-pop-radius); background: var(--page-pop-bg); box-shadow: var(--page-pop-shadow); color: var(--page-pop-ink); font-family: var(--page-font); font-size: 13px; text-align: start; }
204
+ .page-chart-panel:focus { outline: none; }
205
+ .page-chart-row { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
206
+ .page-chart-row:last-child { margin-bottom: 0; }
207
+ .page-chart-label { color: var(--page-pop-muted); font-size: 12px; font-weight: 500; }
208
+ .page-chart-types { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; }
209
+ .page-chart-type, .page-chart-remove, .page-chart-done { display: inline-flex; align-items: center; justify-content: center; gap: 6px; box-sizing: border-box; height: 28px; padding: 0 10px; border: 1px solid var(--page-pop-line); border-radius: var(--page-chrome-radius); background: none; color: var(--page-pop-ink); font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
210
+ .page-chart-type:hover, .page-chart-remove:hover, .page-chart-done:hover { background: var(--page-pop-hover); }
211
+ .page-chart-type[aria-pressed="true"] { border-color: var(--page-chrome-line-moved); background: var(--page-pop-hover); font-weight: 600; }
212
+ .page-chart-select, .page-chart-title-input { box-sizing: border-box; width: 100%; height: 28px; padding: 0 8px; border: 1px solid var(--page-pop-line); border-radius: var(--page-chrome-radius); background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 12.5px; }
213
+ .page-chart-values { display: flex; flex-direction: column; gap: 1px; padding: 5px 6px; border: 1px solid var(--page-pop-line); border-radius: var(--page-chrome-radius); }
214
+ .page-chart-value { display: flex; align-items: center; gap: 7px; padding: 2px 4px; font-size: 12.5px; cursor: pointer; }
215
+ .page-chart-value:hover { background: var(--page-pop-hover); }
216
+ .page-chart-value input { margin: 0; accent-color: var(--page-accent); }
217
+ .page-chart-swatch { flex: none; width: 10px; height: 10px; border-radius: 2px; }
218
+ .page-chart-hint { color: var(--page-pop-muted); font-size: 11px; line-height: 1.4; }
219
+ .page-chart-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--page-pop-line); }
220
+ .page-chart-remove { color: var(--page-danger); }
221
+ .page-chart-broken { box-sizing: border-box; padding: 16px; border: 1px solid var(--page-pop-line); border-radius: var(--page-chrome-radius); background: var(--page-pop-hover); font-family: var(--page-font); text-align: start; }
222
+ .page-chart-broken[hidden] { display: none; }
223
+ .page-chart-plot[hidden] { display: none; }
224
+ .page-chart-broken-head { margin-bottom: 4px; font-size: 13.5px; font-weight: 600; line-height: 1.3; }
225
+ .page-chart-broken-text { margin: 0 0 12px; color: var(--page-pop-muted); font-size: 12.5px; line-height: 1.45; }
226
+ .page-chart-broken-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
227
+ .page-chart-use, .page-chart-delete { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; height: 28px; padding: 0 10px; border: 1px solid var(--page-pop-line); border-radius: var(--page-chrome-radius); background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
228
+ .page-chart-use:hover, .page-chart-delete:hover { background: var(--page-pop-bg); border-color: var(--page-chrome-line-moved); }
229
+ .page-chart-delete { color: var(--page-danger); }
230
+ .page-chart-broken-actions .page-chart-select { width: auto; min-width: 0; flex: 1 1 140px; }
231
+ @media (max-width: 560px) {
232
+ .page-chart-panel { position: static; inset-inline-end: auto; width: auto; border: 0; border-top: 1px solid var(--page-pop-line); border-radius: 0; box-shadow: none; }
233
+ .page-chart-use, .page-chart-delete, .page-chart-broken-actions .page-chart-select { height: 44px; }
234
+ }
172
235
  .page-table-scroll { max-width: 100%; overflow-x: auto; }
173
236
  .page-table table { border-collapse: collapse; min-width: max-content; width: 100%; table-layout: auto; }
174
237
  .page-table th, .page-table td { min-width: 120px; padding: 6px 10px; border-inline-end: 1px solid var(--page-rule); border-block-end: 1px solid var(--page-rule); text-align: start; white-space: pre-wrap; }
@@ -176,16 +239,62 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
176
239
  .page-table th[data-kind="number"], .page-table td[data-kind="number"] { text-align: end; }
177
240
  .page-table th[aria-sort="ascending"] button::after { content: " ↑"; }
178
241
  .page-table th[aria-sort="descending"] button::after { content: " ↓"; }
179
- .page-table th button, .page-table-row-menu, .page-check-chip { border: 0; border-radius: 5px; background: none; color: inherit; font: inherit; cursor: pointer; }
180
- .page-table th button:hover, .page-table-row-menu:hover, .page-check-chip:hover { background: var(--page-pop-hover); }
181
- .page-table-row-menu { opacity: 0; font-size: 12px; color: var(--page-ink-soft); }
242
+ .page-table th button, .page-table-row-menu { border: 0; border-radius: 5px; background: none; color: inherit; font: inherit; cursor: pointer; }
243
+ .page-table th button:hover, .page-table-row-menu:hover { background: var(--page-pop-hover); }
244
+ .page-editor td > span { display: block; }
245
+ .page-table-row-menu { float: inline-end; margin-inline-start: 4px; opacity: 0; font-size: 12px; color: var(--page-ink-soft); display: inline-grid; place-items: center; width: 22px; height: 22px; padding: 0; }
182
246
  .page-table tr:hover .page-table-row-menu, .page-table tr:focus-within .page-table-row-menu, .page-table-row-menu:focus-visible { opacity: 1; }
183
- .page-check-controls { display: inline-flex; gap: 4px; margin-inline-start: 8px; color: var(--page-ink-soft); font-size: 0.78em; text-decoration: none; }
184
- .page-check-controls[data-empty="true"] { opacity: 0; }
185
- .page-li:hover .page-check-controls, .page-li:focus-within .page-check-controls { opacity: 1; }
247
+ /* The tags float to the end of the item's first line, so long text wraps beside and
248
+ below them without the text's own marks being pulled apart. The item's whole line
249
+ stays the click target for an empty item, which has no text of its own. */
250
+ .page-li.page-check-placeholder::after { content: attr(data-placeholder); position: absolute; inset-inline-start: calc((var(--li-indent) + 1) * var(--page-indent)); color: var(--page-ink-faint); pointer-events: none; }
251
+ .page-check-controls { display: inline-flex; float: inline-end; flex-wrap: wrap; align-items: center; gap: 5px; margin-inline-start: 10px; text-decoration: none; }
252
+ .page-check-chip { display: inline-flex; align-items: center; gap: 4px; box-sizing: border-box; height: 23px; padding: 0 7px; border: 1px solid var(--page-rule); border-radius: var(--page-check-radius); background: transparent; color: var(--page-ink-soft); font: 400 12px/1 var(--page-font); white-space: nowrap; cursor: pointer; }
253
+ .page-check-chip svg { flex: none; }
254
+ .page-check-chip:hover { background: var(--page-pop-hover); }
255
+ .page-check-chip[data-filled="true"] { border-color: transparent; background: var(--page-chrome-bg-moved); color: var(--page-ink); font-weight: 500; }
256
+ .page-check-chip[data-overdue="true"] { border-color: var(--page-danger); color: var(--page-danger); }
257
+ .page-li[data-checked="true"] .page-check-controls { opacity: 0.62; }
258
+ [data-assignees="false"] .page-check-chip[data-check="owner"], [data-dates="false"] .page-check-chip[data-check="date"] { display: none; }
259
+ [data-assignees="false"][data-dates="false"] .page-check-controls { display: none; }
186
260
  .page-check-popup { display: inline-flex; gap: 4px; padding: 4px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); border-radius: var(--page-pop-radius); box-shadow: var(--page-pop-shadow); }
187
261
  .page-check-popup input { font: inherit; color: inherit; background: var(--page-pop-bg); border: 1px solid var(--page-pop-line); }
188
- .page-check-popup.page-check-owner { display: inline-block; width: min(280px, 80vw); padding: 8px 0; vertical-align: top; }
262
+ .page-check-popup.page-check-owner, .page-check-popup.page-check-date { position: absolute; z-index: 5; display: block; box-sizing: border-box; width: min(264px, calc(100vw - 28px)); padding: 8px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); border-radius: var(--page-pop-radius); box-shadow: var(--page-pop-shadow); font-family: var(--page-font); }
263
+ .page-check-search { box-sizing: border-box; width: 100%; height: 28px; padding: 0 8px; border: 1px solid var(--page-pop-line); border-radius: 7px; background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 12.5px; }
264
+ .page-check-search::placeholder { color: var(--page-ink-faint); }
265
+ .page-check-subhead { padding: 7px 4px 4px; color: var(--page-pop-muted); font-size: 11.5px; font-weight: 500; }
266
+ .page-check-people { display: flex; flex-direction: column; gap: 1px; }
267
+ .page-check-person { display: flex; align-items: center; gap: 7px; width: 100%; padding: 5px 6px; border: 0; background: none; color: var(--page-pop-ink); font: inherit; font-size: 12.5px; text-align: start; cursor: pointer; }
268
+ .page-check-person:hover { background: var(--page-pop-hover); }
269
+ .page-check-person[aria-pressed="true"] { font-weight: 500; }
270
+ .page-check-person .page-board-face { flex: 0 0 18px; width: 18px; height: 18px; }
271
+ .page-check-person-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
272
+ .page-check-person-meta { margin-inline-start: auto; color: var(--page-pop-muted); font-size: 11px; }
273
+ .page-check-person svg { flex: none; }
274
+ .page-check-divider { height: 1px; margin: 6px 0; background: var(--page-pop-line); }
275
+ .page-check-danger { color: var(--page-danger); }
276
+ .page-check-danger:hover { background: var(--page-pop-hover); }
277
+ .page-check-danger[disabled] { opacity: 0.45; background: none; cursor: default; }
278
+ .page-check-quick { display: flex; flex-direction: column; gap: 1px; }
279
+ .page-check-quick-row { display: flex; align-items: center; gap: 7px; width: 100%; padding: 5px 6px; border: 0; background: none; color: var(--page-pop-ink); font: inherit; font-size: 12.5px; text-align: start; cursor: pointer; }
280
+ .page-check-quick-row:hover { background: var(--page-pop-hover); }
281
+ .page-check-quick-row svg { flex: none; }
282
+ .page-check-quick-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
283
+ .page-check-quick-meta { margin-inline-start: auto; color: var(--page-pop-muted); font-family: var(--page-font-mono); font-size: 11px; font-variant-numeric: tabular-nums; }
284
+ .page-check-cal { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--page-pop-line); }
285
+ .page-check-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 5px; padding: 0 2px; }
286
+ .page-check-cal-month { color: var(--page-pop-ink); font-size: 12px; font-weight: 600; }
287
+ .page-check-cal-year { font-family: var(--page-font-mono); font-size: 11.5px; font-variant-numeric: tabular-nums; }
288
+ .page-check-cal-nav { display: inline-flex; gap: 3px; }
289
+ .page-check-cal-btn { display: inline-grid; place-content: center; width: 22px; height: 22px; padding: 0; border: 1px solid var(--page-pop-line); border-radius: var(--page-check-radius); background: none; color: var(--page-pop-ink); cursor: pointer; }
290
+ .page-check-cal-btn:hover { background: var(--page-pop-hover); }
291
+ .page-check-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
292
+ .page-check-cal-dow { padding: 2px 0; color: var(--page-pop-muted); font-size: 10.5px; font-weight: 500; }
293
+ .page-check-cal-day { height: 28px; padding: 0; border: 1px solid transparent; border-radius: var(--page-check-radius); background: transparent; color: var(--page-pop-ink); font-family: var(--page-font-mono); font-size: 11.5px; font-variant-numeric: tabular-nums; cursor: pointer; }
294
+ .page-check-cal-day:hover { background: var(--page-pop-hover); }
295
+ .page-check-cal-day[data-outside="true"] { opacity: 0.5; }
296
+ .page-check-cal-day[data-today="true"] { border-color: var(--page-accent); font-weight: 500; }
297
+ .page-check-cal-day[aria-pressed="true"] { background: var(--page-chrome-bg-moved); font-weight: 500; }
189
298
  .page-board-owners { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-inline: 8px; }
190
299
  .page-board-people { display: flex; flex-wrap: wrap; gap: 4px; }
191
300
  .page-board-person { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px 0 4px; border: 1px solid var(--page-rule); border-radius: 14px; background: var(--page-pop-bg); color: var(--page-pop-ink); font: inherit; font-size: 13px; cursor: pointer; transition: background-color var(--page-fade), border-color var(--page-fade); }
@@ -211,6 +320,7 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
211
320
  .page-bubble { display: none; }
212
321
  .page-scrim { display: block; position: fixed; inset: 0; z-index: 4; background: var(--page-scrim); }
213
322
  .page-menu { position: fixed; left: 0; top: 0; width: var(--page-pop-width, min(280px, calc(100vw - 16px))); max-height: min(70vh, var(--page-pop-max, 70vh)); overflow-y: auto; transform: translate(var(--page-pop-x, 0px), var(--page-pop-y, 0px)); z-index: 5; }
323
+ .page-settings { position: fixed; left: 0; top: 0; transform: translate(var(--page-pop-x, 0px), var(--page-pop-y, 0px)); z-index: 5; }
214
324
  .page-menu-item { min-height: 48px; font-size: 16px; }
215
325
  .page-link { position: fixed; left: 0; right: 0; top: auto; bottom: 0; max-width: none; padding: 8px 16px calc(8px + env(safe-area-inset-bottom)); border-radius: 0; border-width: 1px 0 0; }
216
326
  .page-link-input { min-width: 0; height: 44px; font-size: 16px; }
@@ -218,7 +328,12 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
218
328
  .page-image-frame { width: 100% !important; }
219
329
  .page-image-handle { display: none; }
220
330
  .page-table th button, .page-table-row-menu, .page-check-chip, .page-check-popup button, .page-menu-item { min-height: 44px; }
331
+ .page-check-person { min-height: 44px; }
332
+ .page-check-popup .page-check-quick-row { min-height: 44px; }
333
+ .page-check-popup .page-check-cal-day { height: 40px; min-height: 40px; }
221
334
  .page-board-owner-input { font-size: 16px; height: 44px; }
335
+ .page-check-search { font-size: 16px; height: 44px; }
336
+ .page-check-popup .page-check-cal-btn { width: 44px; height: 44px; }
222
337
  .page-board-person { height: 44px; border-radius: 22px; }
223
338
  }
224
339
  @media (prefers-reduced-motion: reduce) {
@@ -232,6 +347,18 @@ const insertHardBreak = (st, dispatch) => {
232
347
  return true;
233
348
  };
234
349
 
350
+ // Mod-u belongs to an editable page selection only: anywhere else the browser
351
+ // keeps its own meaning for the key. The write itself goes through the format
352
+ // gate, so a refused activation leaves the document exactly as it was.
353
+ const underlineShortcut = (st, dispatch, view) => {
354
+ if (!dispatch || !view?.editable || !markable(st, schema.marks.underline)) return false;
355
+ import("./format-native-commands.js")
356
+ .then(({ formatMark, refuse }) => formatMark(view, "underline").then((result) => {
357
+ if (result?.status !== "applied") refuse(view, result?.message ?? "That format isn\u2019t available here.");
358
+ }), () => null);
359
+ return true;
360
+ };
361
+
235
362
  const editKeys = ({ history: useHistory = true, undo, redo } = {}) => ({
236
363
  "Mod-z": useHistory ? history.undo : () => { undo?.(); return true; },
237
364
  "Shift-Mod-z": useHistory ? history.redo : () => { redo?.(); return true; },
@@ -239,6 +366,7 @@ const editKeys = ({ history: useHistory = true, undo, redo } = {}) => ({
239
366
  "Shift-Enter": insertHardBreak,
240
367
  "Mod-b": commands.toggleMark(schema.marks.strong),
241
368
  "Mod-i": commands.toggleMark(schema.marks.em),
369
+ "Mod-u": underlineShortcut,
242
370
  });
243
371
 
244
372
  const restoreShortcut = (st, dispatch, view) => {
@@ -325,7 +453,7 @@ function featureWatchPlugin(features) {
325
453
  const walk = () => {
326
454
  for (const id of failed) if (features.has(id)) failed.delete(id);
327
455
  editorView.state.doc.forEach((node) => {
328
- const id = featureForType(node.type.name);
456
+ const id = featureForNode(node);
329
457
  if (!id || features.has(id) || failed.has(id)) return;
330
458
  features.ensure(id).catch(() => { failed.add(id); });
331
459
  });
@@ -419,11 +547,15 @@ function createCommandHost({ load = () => Promise.reject(new Error("page: this e
419
547
  }
420
548
  }
421
549
 
422
- function leave() {
550
+ function quiesce() {
423
551
  if (!active) return;
424
552
  active = false;
425
553
  for (const waiter of waiters) waiter.controller.abort({ ok: false, code: "not-editing" });
426
554
  waiters.clear();
555
+ }
556
+
557
+ function leave() {
558
+ quiesce();
427
559
  loader?.dispose();
428
560
  loader = null;
429
561
  }
@@ -433,6 +565,7 @@ function createCommandHost({ load = () => Promise.reject(new Error("page: this e
433
565
  observe,
434
566
  run,
435
567
  enter() { if (!disposed) active = true; },
568
+ quiesce,
436
569
  leave,
437
570
  dispose() {
438
571
  leave();
@@ -447,10 +580,19 @@ const commandHosts = new WeakMap();
447
580
  // A command asks for its module with the state it was asked in, so the host has to hear
448
581
  // about every transaction: one that moves the document revision or the selection refuses
449
582
  // a command whose module is still arriving rather than running it where the person has
450
- // since left.
583
+ // since left. It also holds the one typed key a command owns: `@` asks for the mention
584
+ // picker, and the host types the character itself so the command is handed the bookmark it
585
+ // belongs to, with everything else about that picker left to the module that arrives for
586
+ // it. It is asked before the plugin that records ordinary typing, since it types it.
451
587
  function commandWatchPlugin(host) {
452
588
  return new state.Plugin({
453
589
  view: () => ({ update: (editorView, prevState) => host.observe(editorView.state, prevState) }),
590
+ props: { handleTextInput(v, from, to, text) {
591
+ if (text !== "@" || v.composing || !Object.hasOwn(COMMAND_FEATURES, "mentions")) return false;
592
+ v.dispatch(v.state.tr.insertText("@", from, to));
593
+ host.run("mentions", { from, to: from + 1 });
594
+ return true;
595
+ } },
454
596
  });
455
597
  }
456
598
 
@@ -479,6 +621,7 @@ export function pagePlugins({ upload = defaultUpload, highlight, history: useHis
479
621
  return [
480
622
  announcementPlugin(),
481
623
  chromePlugin({ onAiTool }),
624
+ commandHost ? commandWatchPlugin(commandHost) : null,
482
625
  slashRangePlugin(),
483
626
  toolFollowPlugin(toolHost),
484
627
  linkPlugin(),
@@ -497,9 +640,7 @@ export function pagePlugins({ upload = defaultUpload, highlight, history: useHis
497
640
  structurePlugin(),
498
641
  identityPlugin(),
499
642
  tablePlugin(),
500
- chartPlugin(),
501
- boardPlugin(),
502
- checklistPlugin(),
643
+ viewHookPlugin(),
503
644
  csvImportPlugin({ upload }),
504
645
  collapsePlugin(),
505
646
  mediaPlugin({ upload, features }),
@@ -510,7 +651,6 @@ export function pagePlugins({ upload = defaultUpload, highlight, history: useHis
510
651
  gapcursor.gapCursor(),
511
652
  lazyViewsPlugin(),
512
653
  features ? featureWatchPlugin(features) : null,
513
- commandHost ? commandWatchPlugin(commandHost) : null,
514
654
  ].filter(Boolean);
515
655
  }
516
656
 
@@ -531,14 +671,16 @@ export function refreshMountedView(view) {
531
671
 
532
672
  // Admission on a mounted editor, in one place: the commit that enters edit opens it,
533
673
  // and the exit that holds the session closed shuts it without touching a buffer.
534
- export function setEditorActive(view, active) {
674
+ export function setEditorActive(view, active, { quiesce = false } = {}) {
535
675
  if (!view || view.isDestroyed) return false;
536
676
  const open = active === true;
537
677
  view.setProps({ editable: () => open });
538
678
  // A refused exit closes admission without destroying the view, so the commands are told
539
- // here: the exit disposes what they installed, and the next entry installs fresh.
679
+ // here: a quiesce keeps what they installed and their pending registrations for the
680
+ // resume, and only a committed exit disposes them.
540
681
  const host = commandHosts.get(view);
541
682
  if (open) host?.enter();
683
+ else if (quiesce) host?.quiesce();
542
684
  else host?.leave();
543
685
  return open;
544
686
  }
@@ -563,7 +705,7 @@ function checkboxClick(view, event) {
563
705
  // Mounts the editor in `mount`, inside a .page-shell wrapper (the surface a
564
706
  // theme styles). `onChange(record)` gets the block record (blockMap) after
565
707
  // every transaction that changed the document.
566
- export function createPageEditor(mount, { blocks = null, onChange = null, upload, highlight, doc: initialDoc, dispatch, history: useHistory = true, undo, redo, editable = true, toolHost = null, onAiTool = null, reviseTool = null, board = null, features: providedFeatures = null } = {}) {
708
+ export function createPageEditor(mount, { blocks = null, onChange = null, upload, highlight, doc: initialDoc, dispatch, history: useHistory = true, undo, redo, editable = true, toolHost = null, onAiTool = null, reviseTool = null, board = null, features: providedFeatures = null, formatGate = null } = {}) {
567
709
  const doc = mount.ownerDocument;
568
710
  // Editor chrome, injected on every edit-mode boot and kept out of the saved file:
569
711
  // the reader theme <link> styles the page for visitors.
@@ -586,9 +728,7 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
586
728
  context: (editorView) => ({ view: editorView, upload }),
587
729
  });
588
730
  const nodeViewRegistry = new Map();
589
- const nodeViews = {
590
- chart: (node, v, getPos) => new ChartView(node, v, getPos),
591
- };
731
+ const nodeViews = {};
592
732
  for (const feature of Object.values(FEATURES)) for (const type of feature.views ?? feature.types) {
593
733
  nodeViews[type] = (node, v, getPos, decorations, inner) => {
594
734
  const make = nodeViewRegistry.get(node.type.name);
@@ -598,6 +738,10 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
598
738
  view = new pmView.EditorView(shell, {
599
739
  state: pageState(blocks, { upload, highlight, doc: initialDoc, history: useHistory, undo, redo, onAiTool, toolHost, features, commandHost }),
600
740
  attributes: { class: "page-editor" },
741
+ // The phone keeps a fixed bar and the plus and grip at the bottom: a caret scrolled into
742
+ // view stops clear of them.
743
+ scrollMargin: { top: 16, bottom: 96, left: 5, right: 5 },
744
+ scrollThreshold: { top: 16, bottom: 96, left: 0, right: 0 },
601
745
  editable: () => editable,
602
746
  nodeViews,
603
747
  handleDOMEvents: {
@@ -622,9 +766,41 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
622
766
  },
623
767
  });
624
768
  nodeViewRegistries.set(view, nodeViewRegistry);
769
+ viewFeatures.set(view, features);
625
770
  commandHost.attach(view);
626
771
  commandHosts.set(view, commandHost);
627
772
  const destroy = view.destroy.bind(view);
628
- view.destroy = () => { commandHost.dispose(); features.dispose(); destroy(); };
773
+ // A new-format command asks the format gate, which the page plugin hands over as a
774
+ // loader of its own: neither the gate nor its host adapter is on the editor's ready
775
+ // path, so a reader who never uses a newer format never fetches them. The gate is
776
+ // created once, on the first command, and belongs to this view.
777
+ let gateHeld = null;
778
+ let disposed = false;
779
+ const heldGate = () => {
780
+ if (!gateHeld) {
781
+ gateHeld = Promise.resolve().then(() => formatGate()).catch((error) => {
782
+ gateHeld = null;
783
+ throw error;
784
+ });
785
+ }
786
+ return gateHeld;
787
+ };
788
+ view.runFormatCommand = async (apply) => {
789
+ if (disposed) return { status: "disposed", retry: false };
790
+ if (!formatGate) return { status: "unsupported-host", code: "page-format-unsupported", retry: false };
791
+ const gate = await heldGate();
792
+ if (disposed) {
793
+ gate.dispose?.();
794
+ return { status: "disposed", retry: false };
795
+ }
796
+ return gate.run({ apply });
797
+ };
798
+ view.destroy = () => {
799
+ disposed = true;
800
+ if (gateHeld) gateHeld.then((gate) => gate?.dispose?.(), () => {});
801
+ commandHost.dispose();
802
+ features.dispose();
803
+ destroy();
804
+ };
629
805
  return view;
630
806
  }