@panphora/clayjs 1.15.2 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/README.md +4 -5
  2. package/THIRD-PARTY-NOTICES.md +16 -0
  3. package/compat/highlight-11.9.0.min.js +1213 -0
  4. package/compat/highlight-LICENSE +29 -0
  5. package/compat/journal-assets.lock.json +22 -0
  6. package/compat/overtype-2.6.1.min.js +116 -0
  7. package/compat/overtype-LICENSE +21 -0
  8. package/dist/all.js +90 -0
  9. package/dist/clay-dom.js +84 -0
  10. package/dist/clay-events.js +103 -0
  11. package/dist/clay-internals.js +85 -0
  12. package/dist/clay-options.js +93 -0
  13. package/dist/clay-router.js +98 -0
  14. package/dist/clay-ui.js +95 -0
  15. package/dist/clay-utils.js +84 -0
  16. package/dist/clay.js +146 -0
  17. package/dist/clay.standalone.js +83090 -58706
  18. package/dist/m/attrs/add-from-T5LJXFFI.js +1 -0
  19. package/dist/m/attrs/onaftersave-OKSWEJQF.js +1 -0
  20. package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +1 -0
  21. package/dist/m/attrs/save-freeze-FUP4Q4X3.js +1 -0
  22. package/dist/m/c-2AZDZWCK.js +1 -0
  23. package/dist/m/c-2JL6E75N.js +1 -0
  24. package/dist/m/c-2M73252O.js +1 -0
  25. package/dist/m/c-2SPVQSHC.js +1 -0
  26. package/dist/m/c-33HUYEDI.js +1 -0
  27. package/dist/m/c-33JJ6CBX.js +2 -0
  28. package/dist/m/c-35NBGMZB.js +1 -0
  29. package/dist/m/c-3GJ4SDE2.js +1 -0
  30. package/dist/m/c-3IQRECYS.js +1 -0
  31. package/dist/m/c-3OWDMNJR.js +2 -0
  32. package/dist/m/c-46XYM55L.js +1 -0
  33. package/dist/m/c-4B6H4G5V.js +1 -0
  34. package/dist/m/c-4DRMS4QG.js +9 -0
  35. package/dist/m/c-4FFHZM32.js +1 -0
  36. package/dist/m/c-4JZDZFF6.js +1 -0
  37. package/dist/m/c-4L3BTFRK.js +1 -0
  38. package/dist/m/c-4Q2XTQ2N.js +2 -0
  39. package/dist/m/c-4WKFKEKF.js +1 -0
  40. package/dist/m/c-4YSCRICO.js +1 -0
  41. package/dist/m/c-54RCDCBU.js +1 -0
  42. package/dist/m/c-5M4YEKCX.js +4 -0
  43. package/dist/m/c-5R4PTRG2.js +1 -0
  44. package/dist/m/c-5V3MOVJT.js +1 -0
  45. package/dist/m/c-5VYOWDTI.js +1 -0
  46. package/dist/m/c-5Y6TNX5W.js +34 -0
  47. package/dist/m/c-5YIHJ3TJ.js +1 -0
  48. package/dist/m/c-66O32MV3.js +28 -0
  49. package/dist/m/c-6HMB24EV.js +1 -0
  50. package/dist/m/c-7DJDKPGL.js +1 -0
  51. package/dist/m/c-7O6AFR65.js +1 -0
  52. package/dist/m/c-7SWMYKAI.js +1 -0
  53. package/dist/m/c-7VGZ7RU5.js +1 -0
  54. package/dist/m/c-A7LS5UZW.js +1 -0
  55. package/dist/m/c-ANQTJ3B5.js +2 -0
  56. package/dist/m/c-AQLFEATB.js +1 -0
  57. package/dist/m/c-AYVOREBE.js +1 -0
  58. package/dist/m/c-B2ZXPOS3.js +3 -0
  59. package/dist/m/c-BBVGTQG7.js +1 -0
  60. package/dist/m/c-BMIZKMPV.js +1 -0
  61. package/dist/m/c-BTDI4Y23.js +1 -0
  62. package/dist/m/c-BWFVSBDF.js +1 -0
  63. package/dist/m/c-BYTKL6UX.js +1 -0
  64. package/dist/m/c-CFHW5AMY.js +6 -0
  65. package/dist/m/c-CJZSLMVQ.js +1 -0
  66. package/dist/m/c-CYWLPPUS.js +1 -0
  67. package/dist/m/c-CZUSOB7V.js +1 -0
  68. package/dist/m/c-DBE6ORSI.js +1 -0
  69. package/dist/m/c-DCFSEJZG.js +1 -0
  70. package/dist/m/c-DNZ5IFM6.js +1 -0
  71. package/dist/m/c-EDHUR4MX.js +1 -0
  72. package/dist/m/c-EJTGYSLN.js +1 -0
  73. package/dist/m/c-EY6UOM54.js +1 -0
  74. package/dist/m/c-F3CKJTJS.js +1 -0
  75. package/dist/m/c-F7Y6FLPY.js +1 -0
  76. package/dist/m/c-FA4DZVTC.js +1 -0
  77. package/dist/m/c-FILLTJA7.js +1 -0
  78. package/dist/m/c-G2BUZITR.js +1 -0
  79. package/dist/m/c-GB3JTV6E.js +1 -0
  80. package/dist/m/c-GBGWXTX4.js +1 -0
  81. package/dist/m/c-GHBCCVG4.js +1 -0
  82. package/dist/m/c-GJAEGGXY.js +1 -0
  83. package/dist/m/c-GOWBDM6J.js +1 -0
  84. package/dist/m/c-GQ6F6IDK.js +1 -0
  85. package/dist/m/c-H4LG442Q.js +1 -0
  86. package/dist/m/c-HA6INT6J.js +1 -0
  87. package/dist/m/c-HPURYVZS.js +2 -0
  88. package/dist/m/c-HRDCD3WN.js +67 -0
  89. package/dist/m/c-HT33EQRN.js +1 -0
  90. package/dist/m/c-HVYXIS72.js +1 -0
  91. package/dist/m/c-IB52R237.js +1 -0
  92. package/dist/m/c-IJUK2GPW.js +1 -0
  93. package/dist/m/c-IKWNE4R4.js +1 -0
  94. package/dist/m/c-IU3IYEU6.js +1 -0
  95. package/dist/m/c-IZ2EBRAJ.js +1 -0
  96. package/dist/m/c-J7CA54IP.js +1 -0
  97. package/dist/m/c-JAX2667S.js +6 -0
  98. package/dist/m/c-KI7M4AY3.js +1 -0
  99. package/dist/m/c-KIPQJX4B.js +1 -0
  100. package/dist/m/c-KKWLDOPM.js +1 -0
  101. package/dist/m/c-KR4UW4KW.js +1 -0
  102. package/dist/m/c-KY3VJGUA.js +8 -0
  103. package/dist/m/c-L4WQSC27.js +1 -0
  104. package/dist/m/c-LFPHCXY3.js +1 -0
  105. package/dist/m/c-LR4CGEPT.js +91 -0
  106. package/dist/m/c-LRPUO2MX.js +1 -0
  107. package/dist/m/c-LTGMXXKO.js +1 -0
  108. package/dist/m/c-LXENEBV7.js +1 -0
  109. package/dist/m/c-LZJPNR2W.js +1 -0
  110. package/dist/m/c-MMUIUUZG.js +2 -0
  111. package/dist/m/c-MTQHWQC3.js +1 -0
  112. package/dist/m/c-N326R2JD.js +1 -0
  113. package/dist/m/c-N7JPUPGC.js +1 -0
  114. package/dist/m/c-NMJRXKPC.js +8 -0
  115. package/dist/m/c-NRPJEVJS.js +1 -0
  116. package/dist/m/c-NUHP2XDB.js +1 -0
  117. package/dist/m/c-NZMNXVF6.js +1 -0
  118. package/dist/m/c-OCSZ4QGN.js +1 -0
  119. package/dist/m/c-OIBUTDXI.js +1 -0
  120. package/dist/m/c-OIDRZKAF.js +1 -0
  121. package/dist/m/c-OYTFP5QN.js +1 -0
  122. package/dist/m/c-P7KNDG34.js +1 -0
  123. package/dist/m/c-PPOSCAQD.js +1 -0
  124. package/dist/m/c-PQ5LK6QE.js +993 -0
  125. package/dist/m/c-PZ4XEPUP.js +2 -0
  126. package/dist/m/c-QHIWXHRO.js +1 -0
  127. package/dist/m/c-QHWJ5ZIJ.js +1 -0
  128. package/dist/m/c-QIX5BJYN.js +1 -0
  129. package/dist/m/c-QNKREOL6.js +1 -0
  130. package/dist/m/c-QQHEX6HA.js +1 -0
  131. package/dist/m/c-R7RMANJI.js +9 -0
  132. package/dist/m/c-RI4NT75Y.js +1 -0
  133. package/dist/m/c-RM7J7BV2.js +1 -0
  134. package/dist/m/c-RUJKSUQW.js +1 -0
  135. package/dist/m/c-RVMWGXEJ.js +1 -0
  136. package/dist/m/c-S7H2DRCB.js +1 -0
  137. package/dist/m/c-S7LK4KUE.js +1 -0
  138. package/dist/m/c-SGCCC4FW.js +25 -0
  139. package/dist/m/c-SGIA4IEQ.js +188 -0
  140. package/dist/m/c-SWRJFCNN.js +1 -0
  141. package/dist/m/c-T4VO4OCB.js +1 -0
  142. package/dist/m/c-TCCA5BZN.js +2 -0
  143. package/dist/m/c-TQEMVJCS.js +1 -0
  144. package/dist/m/c-TQZ2JYHD.js +1 -0
  145. package/dist/m/c-TR6RMDQ7.js +3 -0
  146. package/dist/m/c-TRS6TM6O.js +1 -0
  147. package/dist/m/c-UGHH4MB3.js +1 -0
  148. package/dist/m/c-UKHTLWUW.js +1 -0
  149. package/dist/m/c-UL2YUY52.js +1 -0
  150. package/dist/m/c-UNN7LWCT.js +1 -0
  151. package/dist/m/c-UXBMRDZB.js +235 -0
  152. package/dist/m/c-V7PZZRG7.js +1 -0
  153. package/dist/m/c-VAK7SG37.js +1 -0
  154. package/dist/m/c-VDSS4K3Z.js +1 -0
  155. package/dist/m/c-VFX45IZQ.js +1 -0
  156. package/dist/m/c-VHNSBD2M.js +1 -0
  157. package/dist/m/c-VKEDWYDF.js +1 -0
  158. package/dist/m/c-VL52ADDR.js +1 -0
  159. package/dist/m/c-WN5AY3OZ.js +1 -0
  160. package/dist/m/c-WNYBLIOZ.js +1 -0
  161. package/dist/m/c-WRBKEJD4.js +1 -0
  162. package/dist/m/c-WWDBYXLK.js +1 -0
  163. package/dist/m/c-WYFDLITL.js +1 -0
  164. package/dist/m/c-XN6MBKI6.js +67 -0
  165. package/dist/m/c-XNNW2CWK.js +1 -0
  166. package/dist/m/c-XPELB6E5.js +1 -0
  167. package/dist/m/c-XQXZAO7N.js +1 -0
  168. package/dist/m/c-Y6USGTS5.js +5 -0
  169. package/dist/m/c-YDH7Q3NY.js +1 -0
  170. package/dist/m/c-YG2NJYS6.js +135 -0
  171. package/dist/m/c-Z36J63PP.js +1 -0
  172. package/dist/m/c-Z4VRC7GU.js +7 -0
  173. package/dist/m/c-Z6K52OLP.js +19 -0
  174. package/dist/m/c-ZDY2MDAA.js +21 -0
  175. package/dist/m/c-ZE46QDIH.js +1 -0
  176. package/dist/m/c-ZUCOKOHT.js +1 -0
  177. package/dist/m/core/admin-attrs-VGNLAUDK.js +1 -0
  178. package/dist/m/core/autosave-E4PEFPZ6.js +1 -0
  179. package/dist/m/core/conflict-notice-VQ6Y64PH.js +1 -0
  180. package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
  181. package/dist/m/core/host-permission-XN3K2IW3.js +1 -0
  182. package/dist/m/core/is-edit-mode-JXHDVGUL.js +1 -0
  183. package/dist/m/core/persist-HKHA66IY.js +3 -0
  184. package/dist/m/core/recovery-TBBSICE2.js +1 -0
  185. package/dist/m/core/save-WUCHTQBE.js +1 -0
  186. package/dist/m/core/save-core-FJVHT5M3.js +1 -0
  187. package/dist/m/core/snapshot-F3SWDZMM.js +1 -0
  188. package/dist/m/core/unsaved-warning-STKBHE6G.js +1 -0
  189. package/dist/m/dom/all-APS5ZSKP.js +1 -0
  190. package/dist/m/dom/dom-helpers-L7OSR6S4.js +1 -0
  191. package/dist/m/events/index-23DPTUT4.js +1 -0
  192. package/dist/m/internals/index-WOVAP3RU.js +1 -0
  193. package/dist/m/lib/cache-bust-CGWT6ZJI.js +1 -0
  194. package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
  195. package/dist/m/loader-HXX6YNH5.js +1 -0
  196. package/dist/m/manifest-F22DEDAC.js +1 -0
  197. package/dist/m/manifest-edit-C3C7376B.js +1 -0
  198. package/dist/m/manifest.json +3491 -0
  199. package/dist/m/options/options-2LH76JQ3.js +1 -0
  200. package/dist/m/page/editor-XUTB2HB2.js +189 -0
  201. package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +6 -0
  202. package/dist/m/plugins/bind-text-NX46N5UV.js +1 -0
  203. package/dist/m/plugins/cms-HP3LSTPN.js +2293 -0
  204. package/dist/m/plugins/demo-ZIBFAA3R.js +1 -0
  205. package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
  206. package/dist/m/plugins/indicator-PV4YW2ID.js +1 -0
  207. package/dist/m/plugins/live-NUTTXSBS.js +1 -0
  208. package/dist/m/plugins/menu-HCKO3DSP.js +138 -0
  209. package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
  210. package/dist/m/plugins/page-KSUH7G6J.js +2 -0
  211. package/dist/m/plugins/page-view-W73OW7WI.js +1 -0
  212. package/dist/m/plugins/people-AJJ32YCG.js +1 -0
  213. package/dist/m/plugins/quickcrop-QNY3PKLD.js +26 -0
  214. package/dist/m/plugins/quickcrop-QNY3PKLD.js.LEGAL.txt +7 -0
  215. package/dist/m/plugins/richclay-RC3L2F6X.js +517 -0
  216. package/dist/m/plugins/richclay-RC3L2F6X.js.LEGAL.txt +1 -0
  217. package/dist/m/plugins/sortable-YRMQX3ZU.js +1 -0
  218. package/dist/m/plugins/source-KHK5UUSK.js +1 -0
  219. package/dist/m/plugins/undo-OF2L3CXU.js +1 -0
  220. package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
  221. package/dist/m/plugins/upgrade-ZNYODP5Y.js +1 -0
  222. package/dist/m/plugins/upload-XSGNILUY.js +1 -0
  223. package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +21 -0
  224. package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +1 -0
  225. package/dist/m/plugins/wire-ZWVWKF76.js +1 -0
  226. package/dist/m/reader-core-ILWLXVMP.js +68 -0
  227. package/dist/m/route/route-KOCZSEGN.js +1 -0
  228. package/dist/m/sync/live-sync-4AUVXVJU.js +1 -0
  229. package/dist/m/ui/index-EVHKMKBL.js +1 -0
  230. package/dist/m/utils/index-4TJCVLT7.js +1 -0
  231. package/dist/m/vendor/Sortable.vendor-A755ITLN.js +3 -0
  232. package/dist/m/vendor/Sortable.vendor-A755ITLN.js.LEGAL.txt +1 -0
  233. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +6 -0
  234. package/dist/offline-manifest.json +3800 -0
  235. package/dist/page/themes/light-1.css +406 -0
  236. package/dist/server/core.cjs +23662 -22860
  237. package/dist/server/offline-runtime.cjs +4954 -0
  238. package/dist/server/offline-runtime.mjs +4923 -0
  239. package/dist/server/rooms.mjs +9 -0
  240. package/docs/guide.md +1078 -669
  241. package/entries/all.js +2 -0
  242. package/entries/clay-data.js +1 -1
  243. package/entries/clay-dom.js +2 -0
  244. package/entries/clay-events.js +2 -0
  245. package/entries/clay-internals.js +2 -0
  246. package/entries/clay-options.js +2 -0
  247. package/entries/clay-router.js +2 -0
  248. package/entries/clay-ui.js +2 -0
  249. package/entries/clay-utils.js +2 -0
  250. package/entries/clay.js +2 -0
  251. package/entries/sap.js +6 -6
  252. package/fonts/ibm-plex-mono-LICENSE +93 -0
  253. package/fonts/ibm-plex-mono-latin-300-normal.woff2 +0 -0
  254. package/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
  255. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  256. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  257. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  258. package/fonts/ibm-plex-sans-LICENSE +93 -0
  259. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  260. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  261. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  262. package/fonts/ibm-plex.css +71 -0
  263. package/fonts/inter-LICENSE +93 -0
  264. package/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  265. package/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  266. package/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  267. package/fonts/inter-greek-wght-normal.woff2 +0 -0
  268. package/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  269. package/fonts/inter-latin-wght-normal.woff2 +0 -0
  270. package/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  271. package/fonts/inter.css +62 -0
  272. package/fonts/newsreader-LICENSE +93 -0
  273. package/fonts/newsreader-latin-ext-opsz-normal.woff2 +0 -0
  274. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  275. package/fonts/newsreader-vietnamese-opsz-normal.woff2 +0 -0
  276. package/fonts/newsreader.css +26 -0
  277. package/fonts/work-sans-LICENSE +93 -0
  278. package/fonts/work-sans-latin-wght-normal.woff2 +0 -0
  279. package/fonts/work-sans.css +8 -0
  280. package/package.json +20 -3
  281. package/packed-contract.json +1 -0
  282. package/src/attrs/add-from.js +27 -4
  283. package/src/attrs/save-freeze.js +7 -52
  284. package/src/core/admin-attrs.js +119 -12
  285. package/src/core/admin-contenteditable.js +12 -10
  286. package/src/core/admin-inputs.js +14 -15
  287. package/src/core/admin-onclick.js +2 -0
  288. package/src/core/autosave.js +74 -21
  289. package/src/core/conflict-download.js +22 -0
  290. package/src/core/conflict-notice-ui.js +1173 -0
  291. package/src/core/conflict-notice.js +140 -697
  292. package/src/core/edit-mode.js +147 -14
  293. package/src/core/etag.js +30 -12
  294. package/src/core/host-meta.js +72 -8
  295. package/src/core/host-permission.js +29 -0
  296. package/src/core/is-edit-mode.js +145 -12
  297. package/src/core/mode-key.js +125 -0
  298. package/src/core/mode-lifecycle.js +544 -0
  299. package/src/core/mode-receipt.js +30 -0
  300. package/src/core/mode-refusal.js +13 -0
  301. package/src/core/mode-resources.js +52 -0
  302. package/src/core/mode-support.js +82 -0
  303. package/src/core/mode-switch.js +259 -0
  304. package/src/core/mode-transition.js +112 -0
  305. package/src/core/page-data-write.js +305 -0
  306. package/src/core/page-data.js +26 -227
  307. package/src/core/page-format-wire.js +4 -0
  308. package/src/core/page-format.js +58 -0
  309. package/src/core/persist.js +37 -9
  310. package/src/core/preserve-authored.js +114 -0
  311. package/src/core/recovery.js +772 -0
  312. package/src/core/runtime-url.js +105 -0
  313. package/src/core/save-core.js +109 -12
  314. package/src/core/save-replace.js +72 -0
  315. package/src/core/save-size-notice.js +134 -0
  316. package/src/core/save-size.js +140 -0
  317. package/src/core/save.js +805 -118
  318. package/src/core/snapshot.js +121 -11
  319. package/src/core/source-map.js +157 -9
  320. package/src/core/unsaved-warning.js +14 -4
  321. package/src/core/upgrade-run.js +408 -0
  322. package/src/core/version.js +18 -0
  323. package/src/lib/assets.js +30 -0
  324. package/src/lib/autosave-debug-gate.js +50 -0
  325. package/src/lib/autosave-scheduler.js +141 -0
  326. package/src/lib/boot-imports.js +8 -0
  327. package/src/lib/derived.js +3 -1
  328. package/src/lib/dirty-gate.js +15 -0
  329. package/src/lib/doc-storage.js +33 -0
  330. package/src/lib/effect.js +9 -4
  331. package/src/lib/mutation.js +4 -2
  332. package/src/lib/offline-runtime.js +226 -0
  333. package/src/lib/preload.js +48 -0
  334. package/src/lib/root-attrs.js +1 -1
  335. package/src/lib/trash.js +18 -0
  336. package/src/lib/undo/capture-move.js +152 -0
  337. package/src/live/local-channel.js +61 -0
  338. package/src/live/local-lane.js +60 -9
  339. package/src/live/server-lane.js +6 -3
  340. package/src/loader-logic.js +76 -15
  341. package/src/loader.js +218 -30
  342. package/src/page/adopt-boot.js +76 -22
  343. package/src/page/adopt.js +25 -4
  344. package/src/page/awareness.js +230 -0
  345. package/src/page/blocks.js +79 -5
  346. package/src/page/blocksel.js +20 -4
  347. package/src/page/board-face.js +13 -0
  348. package/src/page/board-panel.js +5 -17
  349. package/src/page/board-style.js +2 -13
  350. package/src/page/board-view.js +1 -11
  351. package/src/page/board-views.js +17 -0
  352. package/src/page/board.js +2 -2
  353. package/src/page/boot-pack.js +26 -0
  354. package/src/page/boot.js +237 -0
  355. package/src/page/checklist.js +1 -1
  356. package/src/page/chrome.js +411 -61
  357. package/src/page/csv-import.js +17 -2
  358. package/src/page/document-title-dom.js +40 -0
  359. package/src/page/document-title-runtime.js +76 -0
  360. package/src/page/document-title.js +9 -0
  361. package/src/page/drag.js +48 -13
  362. package/src/page/drop.js +2 -1
  363. package/src/page/editor.js +330 -86
  364. package/src/page/feature-loader.js +72 -0
  365. package/src/page/features/board.js +12 -0
  366. package/src/page/features/code.js +13 -0
  367. package/src/page/features/media.js +290 -0
  368. package/src/page/features/registry.js +23 -0
  369. package/src/page/features/table.js +383 -0
  370. package/src/page/{tool-view.js → features/tool.js} +33 -2
  371. package/src/page/format-codec.js +215 -0
  372. package/src/page/format-gate.js +179 -0
  373. package/src/page/format-level.js +157 -0
  374. package/src/page/highlight-slot.js +23 -0
  375. package/src/page/highlight.js +30 -16
  376. package/src/page/history-intent.js +78 -0
  377. package/src/page/inline.js +18 -25
  378. package/src/page/items.js +52 -11
  379. package/src/page/link.js +1 -0
  380. package/src/page/markdown.js +86 -1
  381. package/src/page/media-views.js +3 -0
  382. package/src/page/media.js +33 -245
  383. package/src/page/outline-cache.js +11 -0
  384. package/src/page/placeholder.js +27 -7
  385. package/src/page/projection.js +196 -50
  386. package/src/page/reader-patch.js +306 -0
  387. package/src/page/recover.js +1 -1
  388. package/src/page/rich/page-tool.js +1 -4
  389. package/src/page/runtime-owner.js +27 -0
  390. package/src/page/save-projection.js +7 -4
  391. package/src/page/schema.js +14 -0
  392. package/src/page/table-rect-key.js +3 -0
  393. package/src/page/table-select.js +4 -3
  394. package/src/page/table-style.js +1 -1
  395. package/src/page/table-views.js +3 -0
  396. package/src/page/table.js +8 -293
  397. package/src/page/themes/light-1.css +139 -2
  398. package/src/page/themes/light-1.source.css +269 -0
  399. package/src/page/tool-capability.js +3 -0
  400. package/src/page/tool-views.js +3 -0
  401. package/src/page/view.js +390 -107
  402. package/src/page/ybind/awareness.js +172 -0
  403. package/src/page/ybind/binding.js +9 -1
  404. package/src/page/ybind/bridge.js +50 -47
  405. package/src/page/ybind/decode.js +52 -19
  406. package/src/page/ybind/doc.js +27 -2
  407. package/src/page/ybind/group.js +12 -3
  408. package/src/page/ybind/index.js +2 -0
  409. package/src/page/ybind/lineage.js +71 -0
  410. package/src/page/ybind/marks.js +25 -1
  411. package/src/page/ybind/migrate.js +48 -13
  412. package/src/page/ybind/moves.js +32 -21
  413. package/src/page/ybind/positions.js +50 -0
  414. package/src/page/ybind/provider.js +84 -22
  415. package/src/page/ybind/recovery-proof.js +191 -0
  416. package/src/page/ybind/recovery-replay.js +130 -0
  417. package/src/page/ybind/tokens.js +15 -6
  418. package/src/page/ybind/walk.js +10 -3
  419. package/src/page/ybind/writer.js +48 -4
  420. package/src/plugins/ai-edit.js +167 -32
  421. package/src/plugins/bind-text.js +218 -0
  422. package/src/plugins/cms.js +93 -0
  423. package/src/plugins/indicator.js +57 -5
  424. package/src/plugins/live.js +282 -34
  425. package/src/plugins/menu/claygoo.svg.js +1 -0
  426. package/src/plugins/menu/seo-head.js +323 -0
  427. package/src/plugins/menu/seo.js +849 -0
  428. package/src/plugins/menu.js +425 -0
  429. package/src/plugins/page-view.js +131 -0
  430. package/src/plugins/page.js +1022 -275
  431. package/src/plugins/people.js +158 -31
  432. package/src/plugins/richclay.js +119 -9
  433. package/src/plugins/sortable.js +370 -60
  434. package/src/plugins/source.js +189 -366
  435. package/src/plugins/undo.js +289 -19
  436. package/src/plugins/unsent.js +132 -0
  437. package/src/plugins/upgrade-run.js +1 -0
  438. package/src/plugins/upgrade.js +210 -0
  439. package/src/plugins/upload-task.js +571 -0
  440. package/src/plugins/upload-ui.js +265 -0
  441. package/src/plugins/upload.js +34 -153
  442. package/src/plugins/visitor-drafts/inert.js +32 -0
  443. package/src/plugins/visitor-drafts/owner-chip.js +3 -0
  444. package/src/plugins/visitor-drafts/prompt.js +2 -2
  445. package/src/plugins/visitor-drafts-owner.js +85 -0
  446. package/src/plugins/visitor-drafts.js +31 -42
  447. package/src/plugins/wire.js +82 -1
  448. package/src/reader-core.js +26 -0
  449. package/src/recovery/conflicts.js +287 -0
  450. package/src/recovery/identity.js +154 -0
  451. package/src/recovery/store.js +792 -0
  452. package/src/recovery/undo.js +257 -0
  453. package/src/server/checkpoint/index.js +68 -4
  454. package/src/server/offline-runtime/index.js +214 -0
  455. package/src/server/rooms/index.js +2 -1
  456. package/src/server/rooms/live-data-rooms.js +2 -2
  457. package/src/server/rooms/page-admission.js +33 -0
  458. package/src/server/rooms/page-lineage.js +56 -0
  459. package/src/server/rooms/page-rebase.js +11 -5
  460. package/src/server/rooms/page-state.js +2 -0
  461. package/src/server/rooms/page-write-admission.js +9 -3
  462. package/src/source/session.js +705 -0
  463. package/src/sync/conflict-revert.js +5 -4
  464. package/src/sync/conflicts-edit.js +119 -0
  465. package/src/sync/conflicts-facade.js +49 -0
  466. package/src/sync/conflicts.js +99 -1
  467. package/src/sync/editing-marker.js +37 -6
  468. package/src/sync/editing-sender.js +8 -1
  469. package/src/sync/live-sync-edit.js +63 -0
  470. package/src/sync/live-sync.js +364 -119
  471. package/src/sync/local-presence.js +187 -0
  472. package/src/sync/merge-declarations.js +10 -0
  473. package/src/sync/merge-engine.js +12 -0
  474. package/src/sync/presence-store.js +412 -0
  475. package/src/sync/presence-target.js +243 -0
  476. package/src/sync/presence.js +72 -41
  477. package/src/sync/saved-handoff.js +200 -0
  478. package/src/sync/saved-stream.js +535 -0
  479. package/src/sync/version-guard.js +92 -0
  480. package/src/sync/wire-profile.js +81 -0
  481. package/src/ui/bevel-controls.js +2 -8
  482. package/src/ui/page-scheme.js +7 -0
  483. package/src/ui/skins/cms.js +1 -1
  484. package/src/ui/theme-config.js +150 -0
  485. package/src/ui/theme.js +6 -138
  486. package/src/ui/toast-lazy.js +11 -0
  487. package/src/vendor/LICENSES-optional.md +253 -0
  488. package/src/vendor/highlight.vendor.js +3 -0
  489. package/src/vendor/hljs-bash.vendor.js +2 -0
  490. package/src/vendor/hljs-css.vendor.js +2 -0
  491. package/src/vendor/hljs-javascript.vendor.js +2 -0
  492. package/src/vendor/hljs-json.vendor.js +2 -0
  493. package/src/vendor/hljs-python.vendor.js +2 -0
  494. package/src/vendor/hljs-xml.vendor.js +2 -0
  495. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  496. package/src/vendor/hyper-html-api-upgrade.vendor.js +5 -0
  497. package/src/vendor/hyper-morph-identity.vendor.js +2 -0
  498. package/src/vendor/hyper-morph-json.vendor.js +3 -0
  499. package/src/vendor/hyper-morph.vendor.js +6 -6
  500. package/src/vendor/hyper-undo.vendor.js +1 -1
  501. package/src/vendor/hypercms.vendor.js +169 -84
  502. package/src/vendor/marked.vendor.js +68 -0
  503. package/src/vendor/overtype.vendor.js +994 -0
  504. package/src/vendor/richclay.vendor.js +17 -17
  505. package/src/vendor/sap-core.vendor.js +5 -5
  506. package/src/vendor/sap-ownership.vendor.js +11 -7
@@ -6,30 +6,30 @@
6
6
  // `page` cascade layer, so a theme is one plain stylesheet that sets tokens on
7
7
  // .page-shell (and adds scoped rules where it needs to); unlayered theme rules
8
8
  // always win. Contract: plans/.../default-page/v1/look/THEME-CONTRACT.md
9
- import { state, view as pmView, keymap, commands, history, gapcursor, inputrules } from "../vendor/prosemirror.vendor.js";
9
+ import { state, view as pmView, model, keymap, commands, history, gapcursor, inputrules } from "../vendor/prosemirror.vendor.js";
10
10
  import { schema } from "./schema.js";
11
11
  import { identityPlugin, docFromBlocks, blockMap, keyBetween, newId } from "./model.js";
12
12
  import { blockKeys, structurePlugin, collapsePlugin, pastePlugin, BlockSelection, STEP } from "./blocks.js";
13
- import { chromePlugin } from "./chrome.js";
13
+ import { chromePlugin, slashRangePlugin } from "./chrome.js";
14
14
  import { announcementPlugin } from "./announce.js";
15
15
  import { linkPlugin } from "./link.js";
16
- import { blockSelectionPlugin } from "./blocksel.js";
17
- import { markdownPlugin, markdownKeys, backspaceToParagraph } from "./markdown.js";
16
+ import { blockSelectionPlugin, asStep } from "./blocksel.js";
17
+ import { markdownPlugin, markdownKeys, backspaceToParagraph, composing } from "./markdown.js";
18
18
  import { placeholderPlugin } from "./placeholder.js";
19
- import { mediaPlugin, ImageView, FileView } from "./media.js";
20
- import { highlightPlugin, CodeBlockView } from "./highlight.js";
21
- import { tableKeys, tablePlugin, TableView, TableCellView } from "./table.js";
19
+ import { titlePlugin } from "./document-title-runtime.js";
20
+ import { mediaPlugin } from "./media.js";
21
+ import { highlightSlot } from "./highlight-slot.js";
22
+ import { tableKeys, tablePlugin } from "./table.js";
22
23
  import { tableSelectPlugin } from "./table-select.js";
23
24
  import { chartPlugin, ChartView } from "./chart.js";
24
- import { BoardView } from "./board-view.js";
25
- import { boardPlugin, cardPanel } from "./board-panel.js";
26
- import { ToolView } from "./tool-view.js";
27
- import { createToolHost } from "./tools.js";
25
+ import { boardPlugin } from "./board-views.js";
28
26
  import { checklistPlugin, checklistKeys } from "./checklist.js";
29
27
  import { csvImportPlugin } from "./csv-import.js";
30
- import { TABLE_CSS } from "./table-style.js";
31
- import { BOARD_CSS } from "./board-style.js";
32
28
  import { styleWithLegacyId } from "../lib/style.js";
29
+ import { historyIntentPlugin } from "./history-intent.js";
30
+ import { createFeatureLoader } from "./feature-loader.js";
31
+ import { COMMAND_FEATURES, FEATURES, featureForType } from "./features/registry.js";
32
+ import { toolViews } from "./tool-views.js";
33
33
 
34
34
  const STYLE_ID = "page-editor-style";
35
35
  const INDENTS = [1, 2, 3, 4, 5, 6, 7, 8];
@@ -126,7 +126,10 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
126
126
  .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
127
  .page-tool:hover, .page-tool[aria-pressed="true"] { background: var(--page-pop-hover); }
128
128
  .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; }
129
131
  .page-tool-sep { width: 1px; height: 16px; margin: 0 3px; background: currentColor; opacity: 0.5; }
132
+ .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; }
130
133
  .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; }
131
134
  .page-plus[aria-expanded="true"] { color: var(--page-chrome-ink-moved); background: var(--page-chrome-bg-moved); }
132
135
  .page-grip { position: absolute; z-index: 2; top: calc(var(--page-plus-top, 0px) - var(--page-grip-size, var(--page-plus-size)) / 2); left: calc(var(--page-plus-left, 0px) - var(--page-plus-size) - 12px - var(--page-grip-size, var(--page-plus-size)) - 2px); width: var(--page-grip-size, var(--page-plus-size)); height: var(--page-grip-size, var(--page-plus-size)); display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--page-chrome-radius); background: none; color: var(--page-chrome-ink-moved); opacity: 0; cursor: grab; touch-action: none; transition: opacity var(--page-fade); }
@@ -157,40 +160,8 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
157
160
  .page-link-btn { display: inline-grid; place-items: center; min-width: 30px; height: 30px; padding: 0 6px; border: 0; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
158
161
  .page-link-btn:hover { background: var(--page-pop-hover); }
159
162
  .page-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
160
- .page-editor .page-image { margin-inline: 0; }
161
- .page-image-frame { position: relative; margin: 0 auto; max-width: 100%; }
162
- .page-image-frame img { display: block; width: 100%; height: auto; border-radius: var(--page-image-radius, 6px); }
163
- .page-image[data-broken] .page-image-frame { min-height: 80px; background: var(--page-code-bg); border-radius: var(--page-image-radius, 6px); }
164
- .page-image-handle { position: absolute; top: 50%; inset-inline-end: 6px; width: 6px; height: 36px; margin-top: -18px; border-radius: 3px; background: var(--page-chrome-ink-moved); opacity: 0; cursor: ew-resize; touch-action: none; transition: opacity var(--page-fade); }
165
- .page-image:hover .page-image-handle, .page-image.is-selected .page-image-handle { opacity: 1; }
166
- .page-editor .page-image.page-block-selected { background: none; box-shadow: none; }
167
- .page-editor .page-image.page-block-selected .page-image-frame { box-shadow: 0 0 0 6px var(--page-block-selected, rgba(88, 120, 170, 0.12)); border-radius: var(--page-image-radius, 6px); }
168
- .page-image[data-align="left"] .page-image-frame { margin-left: 0; margin-right: auto; }
169
- .page-image[data-align="right"] .page-image-frame { margin-left: auto; margin-right: 0; }
170
- .page-image[data-align="left"] .page-image-caption { text-align: left; }
171
- .page-image[data-align="right"] .page-image-caption { text-align: right; }
172
- .page-image-align { position: absolute; top: 8px; inset-inline-end: 8px; display: none; gap: 1px; padding: 2px; 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: 7px; }
173
- .page-image.is-selected .page-image-align, .page-image.page-block-selected .page-image-align { display: flex; }
174
- .page-image-align button { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 5px; background: none; color: inherit; cursor: pointer; }
175
- .page-image-align button:hover, .page-image-align button[aria-pressed="true"] { background: var(--page-pop-hover); }
176
- .page-image.is-selected .page-image-frame img { outline: 2px solid var(--page-selection); outline-offset: 2px; }
177
- .page-image-caption { margin-top: 6px; color: var(--page-ink-soft); font-size: 0.88em; text-align: center; outline: none; }
178
- .page-image-caption[data-empty]::before { content: attr(data-placeholder); color: var(--page-ink-faint); }
179
163
  .ProseMirror-focused .page-placeholder::before { content: attr(data-placeholder); float: left; height: 0; color: var(--page-ink-faint); pointer-events: none; }
180
- .page-image:not(.is-selected) .page-image-caption[data-empty]:not(:focus) { display: none; }
181
- .page-file-link { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border: 1px solid var(--page-rule); border-radius: 8px; color: var(--page-ink); text-decoration: none; }
182
- .page-file-icon { color: var(--page-link, var(--page-accent)); }
183
- .page-file-name { color: var(--page-link, var(--page-accent)); min-width: 0; overflow-wrap: anywhere; }
184
- .page-file-meta { margin-inline-start: auto; color: var(--page-ink-faint); font-size: 0.88em; }
185
- .page-upload { display: flex; align-items: center; gap: 10px; margin: var(--page-block-gap) 0; padding: 8px 12px; border: 1px dashed var(--page-rule); border-radius: 8px; color: var(--page-ink-soft); font-size: 0.88em; }
186
- .page-upload-preview { width: 40px; height: 40px; object-fit: cover; border-radius: 4px; opacity: 0.6; }
187
- .page-upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
188
- .page-upload-bar { flex: 1; height: 2px; border-radius: 1px; background: var(--page-rule); overflow: hidden; }
189
- .page-upload-bar > span { display: block; height: 100%; background: var(--page-progress, var(--page-ink-soft)); transition: width 200ms; }
190
- .page-upload[data-state="error"] .page-upload-msg, .page-upload[data-state="embed-choice"] .page-upload-msg { flex: 1; }
191
- .page-upload[data-state="error"] .page-upload-msg { color: var(--page-danger, #9E3B2F); }
192
- .page-upload-btn { border: 0; padding: 4px 8px; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
193
- .page-upload-btn:hover { background: var(--page-pop-hover); }
164
+ .page-editor .page-title-placeholder::before { content: attr(data-placeholder); float: left; height: 0; color: var(--page-ink-faint); pointer-events: none; }
194
165
  .page-csv-error { margin: 8px 0; color: var(--page-danger); font-size: 14px; }
195
166
  .page-editor .page-table { max-width: 100%; min-width: 0; border: 1px solid var(--page-rule); border-radius: 8px; }
196
167
  .page-editor .page-chart { position: relative; max-width: 100%; min-width: 0; margin-inline: 0; color: var(--page-ink); }
@@ -198,14 +169,6 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
198
169
  .page-chart figcaption { color: var(--page-ink-soft); font-size: 0.88em; text-align: center; }
199
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; }
200
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; }
201
- .page-tool-block { position: relative; min-height: 48px; border: 1px solid var(--page-rule); border-radius: 8px; padding: 12px; }
202
- .page-tool-settings { position: absolute; top: 4px; inset-inline-end: 4px; border: 0; border-radius: 5px; background: var(--page-pop-bg); color: var(--page-ink-soft); font: 12px var(--page-font); padding: 5px 8px; cursor: pointer; opacity: 0; }
203
- .page-tool-block:hover .page-tool-settings, .page-tool-block:focus-within .page-tool-settings { opacity: 1; }
204
- .page-ai-panel { position: relative; z-index: 5; width: min(520px, 100%); box-sizing: border-box; margin: 12px auto; padding: 14px; border: 1px solid var(--page-pop-line); border-radius: 10px; background: var(--page-pop-bg); color: var(--page-pop-ink); box-shadow: var(--page-pop-shadow); }
205
- .page-ai-panel textarea { width: 100%; min-height: 88px; box-sizing: border-box; padding: 8px; border: 1px solid var(--page-pop-line); border-radius: 6px; background: var(--page-pop-bg); color: inherit; font: inherit; resize: vertical; }
206
- .page-ai-actions { display: flex; gap: 8px; margin-top: 8px; }
207
- .page-ai-actions button { border: 1px solid var(--page-pop-line); border-radius: 6px; background: var(--page-pop-bg); color: inherit; font: inherit; padding: 5px 10px; cursor: pointer; }
208
- .page-ai-status, [data-tool-note], [data-tool-error] { color: var(--page-danger); font-size: 0.85em; }
209
172
  .page-table-scroll { max-width: 100%; overflow-x: auto; }
210
173
  .page-table table { border-collapse: collapse; min-width: max-content; width: 100%; table-layout: auto; }
211
174
  .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; }
@@ -223,18 +186,31 @@ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-ind
223
186
  .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); }
224
187
  .page-check-popup input { font: inherit; color: inherit; background: var(--page-pop-bg); border: 1px solid var(--page-pop-line); }
225
188
  .page-check-popup.page-check-owner { display: inline-block; width: min(280px, 80vw); padding: 8px 0; vertical-align: top; }
189
+ .page-board-owners { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-inline: 8px; }
190
+ .page-board-people { display: flex; flex-wrap: wrap; gap: 4px; }
191
+ .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); }
192
+ .page-board-person.is-none { padding-inline-start: 10px; color: var(--page-pop-muted); }
193
+ .page-board-person:hover { background: var(--page-pop-hover); }
194
+ .page-board-person[aria-pressed="true"] { border-color: var(--page-accent); background: var(--page-selection); color: var(--page-pop-ink); }
195
+ .page-board-owner-input { width: 100%; height: 32px; box-sizing: border-box; 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: 14px; }
196
+ .page-board-owner-input::placeholder { color: var(--page-ink-faint); }
197
+ .page-board-face { flex: none; display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; background: var(--page-code-bg); box-shadow: inset 0 0 0 1px var(--page-rule); color: var(--page-ink-soft); font-size: 10.5px; font-weight: 600; line-height: 1; }
226
198
  .page-scrim { display: none; }
227
199
  .page-shell [hidden] { display: none !important; }
228
- ${TABLE_CSS}
229
- ${BOARD_CSS}
230
200
  @media (max-width: 560px) {
231
- .page-dock { display: none; }
201
+ /* One fixed bar over the visual viewport, with the four controls a phone keeps.
202
+ The bubble and the desktop-only tools stand down so nothing is doubled. */
203
+ .page-dock { display: flex; position: fixed; left: 0; top: 8px; width: 204px; min-width: 204px; box-sizing: border-box; align-items: center; justify-content: center; gap: 4px; margin: 0; padding: 8px; z-index: 3; }
204
+ .page-tool-desk { display: none; }
205
+ .page-dock .page-tool-more { display: inline-flex; }
206
+ .page-dock .page-tool { min-width: 44px; height: 44px; }
207
+ .page-shell:has(.page-bubble:not([hidden])) .page-dock { visibility: visible; }
208
+ .page-dock + .page-editor { padding-bottom: calc(72px + env(safe-area-inset-bottom)); }
232
209
  .page-plus { position: fixed; top: auto; left: 16px; bottom: calc(16px + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; }
233
- .page-grip { position: fixed; top: auto; left: 68px; bottom: calc(16px + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; opacity: 1; color: var(--page-chrome-ink-rest); background: var(--page-chrome-bg-rest); border: 1px solid var(--page-chrome-line-rest); }
234
- .page-bubble { position: fixed; left: 0; right: 0; top: auto; bottom: 0; transform: none; justify-content: center; gap: 4px; padding: 6px 16px calc(6px + env(safe-area-inset-bottom)); border-radius: 0; border-width: 1px 0 0; background: var(--page-bar-bg); color: var(--page-bar-ink); }
235
- .page-bubble .page-tool { min-width: 44px; height: 44px; }
210
+ .page-grip { position: fixed; top: auto; left: 68px; bottom: calc(16px + var(--page-grip-lift, 0px) + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; opacity: 1; color: var(--page-chrome-ink-rest); background: var(--page-chrome-bg-rest); border: 1px solid var(--page-chrome-line-rest); }
211
+ .page-bubble { display: none; }
236
212
  .page-scrim { display: block; position: fixed; inset: 0; z-index: 4; background: var(--page-scrim); }
237
- .page-menu { position: fixed; left: 0; right: 0; top: auto; bottom: 0; width: auto; max-height: 70vh; z-index: 5; padding: 8px 8px calc(8px + env(safe-area-inset-bottom)); border-radius: var(--page-pop-radius) var(--page-pop-radius) 0 0; border-width: 1px 0 0; }
213
+ .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; }
238
214
  .page-menu-item { min-height: 48px; font-size: 16px; }
239
215
  .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; }
240
216
  .page-link-input { min-width: 0; height: 44px; font-size: 16px; }
@@ -242,9 +218,11 @@ ${BOARD_CSS}
242
218
  .page-image-frame { width: 100% !important; }
243
219
  .page-image-handle { display: none; }
244
220
  .page-table th button, .page-table-row-menu, .page-check-chip, .page-check-popup button, .page-menu-item { min-height: 44px; }
221
+ .page-board-owner-input { font-size: 16px; height: 44px; }
222
+ .page-board-person { height: 44px; border-radius: 22px; }
245
223
  }
246
224
  @media (prefers-reduced-motion: reduce) {
247
- .page-dock, .page-plus, .page-grip, .page-toggle-button, .page-image-handle, .page-upload-bar > span { transition: none; }
225
+ .page-dock, .page-plus, .page-grip, .page-toggle-button, .page-image-handle, .page-upload-bar > span, .page-board-person { transition: none; }
248
226
  }
249
227
  }
250
228
  `;
@@ -263,30 +241,253 @@ const editKeys = ({ history: useHistory = true, undo, redo } = {}) => ({
263
241
  "Mod-i": commands.toggleMark(schema.marks.em),
264
242
  });
265
243
 
244
+ const restoreShortcut = (st, dispatch, view) => {
245
+ if (composing(null, view)) return false;
246
+ return inputrules.undoInputRule(st, dispatch && ((tr) => dispatch(asStep(tr))));
247
+ };
248
+
266
249
  const markKeys = {
267
- Backspace: commands.chainCommands(inputrules.undoInputRule, backspaceToParagraph),
250
+ Backspace: commands.chainCommands(restoreShortcut, backspaceToParagraph),
268
251
  "Mod-e": commands.toggleMark(schema.marks.code),
269
252
  "Mod-Shift-s": commands.toggleMark(schema.marks.strike),
270
253
  };
271
254
 
272
255
  const defaultUpload = (file, options) => globalThis.clay?.upload?.(file, options) ?? Promise.resolve({ ok: false, code: "unavailable", msg: "File uploads are not available in this editor" });
273
256
 
274
- export function pagePlugins({ upload = defaultUpload, highlight, history: useHistory = true, undo, redo, onAiTool = null, toolHost = null } = {}) {
257
+ const nodeViewRegistries = new WeakMap();
258
+
259
+ export const lazyViewsKey = new state.PluginKey("lazyViews");
260
+
261
+ // Stands in for a view part's node view until its type loads: the schema's own
262
+ // toDOM, and an update that asks to be recreated once the real view exists.
263
+ class PendingView {
264
+ constructor(node, registry) {
265
+ this.node = node;
266
+ this.type = node.type;
267
+ this.registry = registry;
268
+ const { dom, contentDOM } = model.DOMSerializer.renderSpec(document, node.type.spec.toDOM(node));
269
+ this.dom = dom;
270
+ this.contentDOM = contentDOM ?? null;
271
+ }
272
+ update(node) { return node.type === this.type && !this.registry.has(node.type.name); }
273
+ }
274
+
275
+ // Recreates only the node views of the types that just loaded: one node
276
+ // decoration makes ProseMirror rebuild those nodes and no others.
277
+ export function lazyViewsPlugin() {
278
+ return new state.Plugin({
279
+ key: lazyViewsKey,
280
+ state: {
281
+ init: () => pmView.DecorationSet.empty,
282
+ apply(tr) {
283
+ const meta = tr.getMeta(lazyViewsKey);
284
+ if (!meta) return pmView.DecorationSet.empty;
285
+ const decorations = [];
286
+ tr.doc.forEach((node, offset) => {
287
+ if (!meta.types.includes(node.type.name)) return;
288
+ decorations.push(pmView.Decoration.node(offset, offset + node.nodeSize, {}, { lazyView: decorations.length + 1 }));
289
+ });
290
+ return pmView.DecorationSet.create(tr.doc, decorations);
291
+ },
292
+ },
293
+ props: { decorations: (state) => lazyViewsKey.getState(state) },
294
+ });
295
+ }
296
+
297
+ // Adds a view part to a running editor: its CSS at once, its node views into the
298
+ // editor's registry, and a transaction that recreates only those block types.
299
+ export function installPageFeature({ view, upload, toolHost, board = null, undo = null, redo = null, revise = null, highlight }, id, mod) {
300
+ const feature = mod.createFeature({ view, upload, toolHost, board, undo, redo, revise, highlight });
301
+ const registry = nodeViewRegistries.get(view);
302
+ const apply = () => {
303
+ if (feature.css) styleWithLegacyId(`page-feature-${id}`, feature.css, `page-feature-${id}`, ["editor-ui"]);
304
+ if (!feature.nodeViews) return;
305
+ const types = Object.keys(feature.nodeViews);
306
+ for (const type of types) registry.set(type, feature.nodeViews[type]);
307
+ view.dispatch(view.state.tr.setMeta(lazyViewsKey, { types }).setMeta("addToHistory", false));
308
+ };
309
+ const afterComposition = () => {
310
+ if (view.isDestroyed) return;
311
+ if (view.composing) setTimeout(afterComposition, 25);
312
+ else apply();
313
+ };
314
+ if (view.composing) view.dom.addEventListener("compositionend", () => setTimeout(afterComposition, 25), { once: true });
315
+ else apply();
316
+ return feature;
317
+ }
318
+
319
+ // Loads the view part for a block type the editor shows but has no view for:
320
+ // the blocks that were there at boot, and any that arrive later.
321
+ function featureWatchPlugin(features) {
322
+ return new state.Plugin({
323
+ view(editorView) {
324
+ const failed = new Set();
325
+ const walk = () => {
326
+ for (const id of failed) if (features.has(id)) failed.delete(id);
327
+ editorView.state.doc.forEach((node) => {
328
+ const id = featureForType(node.type.name);
329
+ if (!id || features.has(id) || failed.has(id)) return;
330
+ features.ensure(id).catch(() => { failed.add(id); });
331
+ });
332
+ };
333
+ let doc = editorView.state.doc;
334
+ walk();
335
+ return {
336
+ update() {
337
+ if (editorView.state.doc === doc) return;
338
+ doc = editorView.state.doc;
339
+ walk();
340
+ },
341
+ };
342
+ },
343
+ });
344
+ }
345
+
346
+ // The optional commands. A command module exports `createFeature(context)` returning
347
+ // `{ run, dispose }`; the editor fetches it on the first ask and installs it once, and
348
+ // nothing here is preloaded by the block-type scan. A command is asked for with the state
349
+ // it was asked in: the module is fetched, and the command runs only if the edit is still
350
+ // open, the document revision has not moved and the selection is where it was. A failed
351
+ // fetch is reported and asked for again on the next request; an exit disposes what was
352
+ // installed and aborts whatever was still arriving, and the next entry installs fresh
353
+ // against the module the browser already holds.
354
+ function createCommandHost({ load = () => Promise.reject(new Error("page: this editor has no command module")), context = () => ({}), reportError } = {}) {
355
+ const modules = new Map();
356
+ const waiters = new Set();
357
+ let view = null;
358
+ let loader = null;
359
+ let active = true;
360
+ let disposed = false;
361
+ let revision = 0;
362
+
363
+ // One fetch per command for the life of the host, whatever the entry: a module that
364
+ // failed is the one thing not kept, so the next ask loads it again.
365
+ const moduleFor = (id) => {
366
+ let fetched = modules.get(id);
367
+ if (!fetched) {
368
+ fetched = Promise.resolve(load(id));
369
+ modules.set(id, fetched);
370
+ fetched.catch(() => { if (modules.get(id) === fetched) modules.delete(id); });
371
+ }
372
+ return fetched;
373
+ };
374
+
375
+ const loaderFor = () => {
376
+ loader ??= createFeatureLoader({
377
+ load: moduleFor,
378
+ install: (id, mod) => mod.createFeature(context(view)),
379
+ reportError: reportError ?? ((error, id) => console.error(`clayjs: the ${id} command failed to load`, error)),
380
+ });
381
+ return loader;
382
+ };
383
+
384
+ // The document revision and the selection a command was asked for. A revision moves
385
+ // when any transaction changes the document, whoever wrote it.
386
+ const leaseOf = () => {
387
+ const at = revision;
388
+ const selection = view.state.selection;
389
+ return {
390
+ revision: at,
391
+ selection,
392
+ valid: () => active && !disposed && !view.isDestroyed && revision === at && view.state.selection.eq(selection),
393
+ };
394
+ };
395
+
396
+ function observe(state, prevState) {
397
+ if (state.doc !== prevState.doc) revision += 1;
398
+ for (const waiter of waiters) {
399
+ if (waiter.lease.valid()) continue;
400
+ waiters.delete(waiter);
401
+ waiter.controller.abort({ ok: false, code: "stale-target" });
402
+ }
403
+ }
404
+
405
+ async function run(id, args) {
406
+ if (!active || disposed || !view || view.isDestroyed) return { ok: false, code: "not-editing" };
407
+ const waiter = { lease: leaseOf(), controller: new AbortController() };
408
+ waiters.add(waiter);
409
+ try {
410
+ const feature = await loaderFor().ensure(id, { signal: waiter.controller.signal });
411
+ if (!feature) return { ok: false, code: "not-editing" };
412
+ if (!waiter.lease.valid()) return { ok: false, code: "stale-target" };
413
+ return await feature.run(args);
414
+ } catch (error) {
415
+ if (waiter.controller.signal.aborted) return waiter.controller.signal.reason;
416
+ return { ok: false, code: "unavailable", error };
417
+ } finally {
418
+ waiters.delete(waiter);
419
+ }
420
+ }
421
+
422
+ function leave() {
423
+ if (!active) return;
424
+ active = false;
425
+ for (const waiter of waiters) waiter.controller.abort({ ok: false, code: "not-editing" });
426
+ waiters.clear();
427
+ loader?.dispose();
428
+ loader = null;
429
+ }
430
+
431
+ return {
432
+ attach(editorView) { view = editorView; },
433
+ observe,
434
+ run,
435
+ enter() { if (!disposed) active = true; },
436
+ leave,
437
+ dispose() {
438
+ leave();
439
+ disposed = true;
440
+ active = false;
441
+ modules.clear();
442
+ },
443
+ };
444
+ }
445
+
446
+ const commandHosts = new WeakMap();
447
+ // A command asks for its module with the state it was asked in, so the host has to hear
448
+ // about every transaction: one that moves the document revision or the selection refuses
449
+ // a command whose module is still arriving rather than running it where the person has
450
+ // since left.
451
+ function commandWatchPlugin(host) {
452
+ return new state.Plugin({
453
+ view: () => ({ update: (editorView, prevState) => host.observe(editorView.state, prevState) }),
454
+ });
455
+ }
456
+
457
+ // Runs an optional command on a mounted editor, by the id its row has in COMMAND_FEATURES.
458
+ // A command nobody offers, or an editor with no command host, refuses rather than throws.
459
+ export function runPageCommand(view, id, args) {
460
+ const host = view ? commandHosts.get(view) : null;
461
+ if (!host) return Promise.resolve({ ok: false, code: "not-editing" });
462
+ if (!Object.hasOwn(COMMAND_FEATURES, id)) return Promise.resolve({ ok: false, code: "unknown-command" });
463
+ return host.run(id, args);
464
+ }
465
+
466
+ // Tools follow the tables a document holds; the host arrives with the tool view
467
+ // part on a document that had no tool at boot.
468
+ function toolFollowPlugin(toolHost) {
469
+ return new state.Plugin({
470
+ view(view) {
471
+ const changed = toolHost ? () => toolHost.tablesChanged() : () => toolViews.get(view)?.tablesChanged?.();
472
+ let previous = view.state.doc;
473
+ return { update(next) { if (next.state.doc !== previous) { previous = next.state.doc; changed(); } } };
474
+ },
475
+ });
476
+ }
477
+
478
+ export function pagePlugins({ upload = defaultUpload, highlight, history: useHistory = true, undo, redo, onAiTool = null, toolHost = null, features = null, commandHost = null } = {}) {
275
479
  return [
276
480
  announcementPlugin(),
277
481
  chromePlugin({ onAiTool }),
278
- toolHost && new state.Plugin({
279
- view(view) {
280
- let previous = view.state.doc;
281
- return { update(next) { if (next.state.doc !== previous) { previous = next.state.doc; toolHost.tablesChanged(); } } };
282
- },
283
- }),
482
+ slashRangePlugin(),
483
+ toolFollowPlugin(toolHost),
284
484
  linkPlugin(),
285
485
  tableSelectPlugin(),
286
486
  blockSelectionPlugin(),
287
487
  markdownPlugin(),
288
488
  markdownKeys(),
289
489
  placeholderPlugin(),
490
+ titlePlugin(),
290
491
  keymap.keymap(markKeys),
291
492
  tableKeys,
292
493
  checklistKeys,
@@ -301,11 +502,15 @@ export function pagePlugins({ upload = defaultUpload, highlight, history: useHis
301
502
  checklistPlugin(),
302
503
  csvImportPlugin({ upload }),
303
504
  collapsePlugin(),
304
- mediaPlugin({ upload }),
305
- highlightPlugin(highlight),
505
+ mediaPlugin({ upload, features }),
506
+ highlightSlot(),
306
507
  pastePlugin(),
508
+ historyIntentPlugin(),
307
509
  useHistory ? history.history() : null,
308
510
  gapcursor.gapCursor(),
511
+ lazyViewsPlugin(),
512
+ features ? featureWatchPlugin(features) : null,
513
+ commandHost ? commandWatchPlugin(commandHost) : null,
309
514
  ].filter(Boolean);
310
515
  }
311
516
 
@@ -315,6 +520,29 @@ export function pageState(blocks, options = {}) {
315
520
  return state.EditorState.create({ doc, plugins: pagePlugins(options) });
316
521
  }
317
522
 
523
+ // The shell is built detached and moved into the article once it is complete, so
524
+ // the node views in it measured nothing while it was out of the document. One pass
525
+ // in place gives them a real box: the state is the same object it was, and no node
526
+ // view is rebuilt for it.
527
+ export function refreshMountedView(view) {
528
+ if (!view || view.isDestroyed) return;
529
+ view.updateState(view.state);
530
+ }
531
+
532
+ // Admission on a mounted editor, in one place: the commit that enters edit opens it,
533
+ // and the exit that holds the session closed shuts it without touching a buffer.
534
+ export function setEditorActive(view, active) {
535
+ if (!view || view.isDestroyed) return false;
536
+ const open = active === true;
537
+ view.setProps({ editable: () => open });
538
+ // 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.
540
+ const host = commandHosts.get(view);
541
+ if (open) host?.enter();
542
+ else host?.leave();
543
+ return open;
544
+ }
545
+
318
546
  // Clicking a checklist box (drawn by ::before in the item's start gutter)
319
547
  // toggles `checked`.
320
548
  function checkboxClick(view, event) {
@@ -335,9 +563,8 @@ function checkboxClick(view, event) {
335
563
  // Mounts the editor in `mount`, inside a .page-shell wrapper (the surface a
336
564
  // theme styles). `onChange(record)` gets the block record (blockMap) after
337
565
  // every transaction that changed the document.
338
- export function createPageEditor(mount, { blocks = null, onChange = null, upload, highlight, doc: initialDoc, dispatch, history: useHistory = true, undo, redo, editable = true, toolHost: providedToolHost = null, onAiTool = null, reviseTool = null, board = null } = {}) {
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 } = {}) {
339
567
  const doc = mount.ownerDocument;
340
- const toolHost = providedToolHost ?? createToolHost({ mode: "edit" });
341
568
  // Editor chrome, injected on every edit-mode boot and kept out of the saved file:
342
569
  // the reader theme <link> styles the page for visitors.
343
570
  styleWithLegacyId(STYLE_ID, PAGE_EDITOR_CSS, STYLE_ID, ["editor-ui"]);
@@ -347,20 +574,32 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
347
574
  shell.setAttribute("sap-ignore", "");
348
575
  mount.appendChild(shell);
349
576
  let afterStep = false;
350
- const view = new pmView.EditorView(shell, {
351
- state: pageState(blocks, { upload, highlight, doc: initialDoc, history: useHistory, undo, redo, onAiTool, toolHost }),
577
+ let view = null;
578
+ const features = providedFeatures ?? createFeatureLoader({
579
+ load: (id) => FEATURES[id].load(),
580
+ install: (id, mod) => installPageFeature({ view, upload, toolHost, board, undo, redo, revise: reviseTool, highlight }, id, mod),
581
+ });
582
+ // Commands are their own loader: a document never fetches a picker to be read or typed
583
+ // in, and the host outlives a refused exit so a re-entry installs once more.
584
+ const commandHost = createCommandHost({
585
+ load: (id) => COMMAND_FEATURES[id].load(),
586
+ context: (editorView) => ({ view: editorView, upload }),
587
+ });
588
+ const nodeViewRegistry = new Map();
589
+ const nodeViews = {
590
+ chart: (node, v, getPos) => new ChartView(node, v, getPos),
591
+ };
592
+ for (const feature of Object.values(FEATURES)) for (const type of feature.views ?? feature.types) {
593
+ nodeViews[type] = (node, v, getPos, decorations, inner) => {
594
+ const make = nodeViewRegistry.get(node.type.name);
595
+ return make ? make(node, v, getPos, decorations, inner) : new PendingView(node, nodeViewRegistry);
596
+ };
597
+ }
598
+ view = new pmView.EditorView(shell, {
599
+ state: pageState(blocks, { upload, highlight, doc: initialDoc, history: useHistory, undo, redo, onAiTool, toolHost, features, commandHost }),
352
600
  attributes: { class: "page-editor" },
353
601
  editable: () => editable,
354
- nodeViews: {
355
- image: (node, v, getPos) => new ImageView(node, v, getPos),
356
- file: (node, v) => new FileView(node, v),
357
- code_block: (node, v, getPos) => new CodeBlockView(node, v, getPos),
358
- table: (node, v, getPos) => new TableView(node, v, getPos),
359
- chart: (node, v, getPos) => new ChartView(node, v, getPos),
360
- tool: (node, v, getPos) => new ToolView(node, v, getPos, { host: toolHost, revise: reviseTool }),
361
- table_cell: (node, v, getPos) => new TableCellView(node, v, getPos),
362
- ...(board ? { board: (node, v, getPos) => new BoardView(node, v, getPos, { commands: board, undo, redo, panel: cardPanel(v) }) } : {}),
363
- },
602
+ nodeViews,
364
603
  handleDOMEvents: {
365
604
  mousedown: checkboxClick,
366
605
  dragstart(v, event) {
@@ -382,5 +621,10 @@ export function createPageEditor(mount, { blocks = null, onChange = null, upload
382
621
  if (onChange && view.state.doc !== before) onChange(blockMap(view.state.doc));
383
622
  },
384
623
  });
624
+ nodeViewRegistries.set(view, nodeViewRegistry);
625
+ commandHost.attach(view);
626
+ commandHosts.set(view, commandHost);
627
+ const destroy = view.destroy.bind(view);
628
+ view.destroy = () => { commandHost.dispose(); features.dispose(); destroy(); };
385
629
  return view;
386
630
  }
@@ -0,0 +1,72 @@
1
+ // Optional editor features load the first time a document needs them. One import per
2
+ // feature; a caller's abort stops only its own wait; dispose() makes late results inert.
3
+ export function createFeatureLoader({ load, install, reportError = (error) => console.error("clayjs: an editor feature failed to load", error) }) {
4
+ const installed = new Map();
5
+ const pending = new Map();
6
+ const prepared = new Map();
7
+ let disposed = false;
8
+
9
+ // A preparation fetches the module without installing it, so the boot can overlap
10
+ // that fetch with the room join. The install that follows consumes this same
11
+ // import: a feature is fetched once, and a failed fetch is that attempt. The next
12
+ // ensure after it loads again through the loader's reporting and the feature's
13
+ // Retry.
14
+ function prepare(id) {
15
+ if (disposed) return Promise.reject(new Error(`page: feature loader disposed before ${id}`));
16
+ let shared = prepared.get(id);
17
+ if (!shared) {
18
+ shared = Promise.resolve(load(id));
19
+ prepared.set(id, shared);
20
+ // The install reports the failure; this keeps an unconsumed preparation quiet.
21
+ shared.catch(() => {});
22
+ }
23
+ return shared;
24
+ }
25
+
26
+ function loadFor(id) {
27
+ const fetched = prepared.get(id);
28
+ if (!fetched) return load(id);
29
+ prepared.delete(id);
30
+ return fetched;
31
+ }
32
+
33
+ function ensure(id, { signal } = {}) {
34
+ if (disposed) return Promise.reject(new Error(`page: feature loader disposed before ${id}`));
35
+ if (installed.has(id)) return Promise.resolve(installed.get(id));
36
+ let shared = pending.get(id);
37
+ if (!shared) {
38
+ shared = loadFor(id)
39
+ .then((mod) => (disposed ? null : Promise.resolve(install(id, mod))))
40
+ .then((feature) => {
41
+ if (disposed) {
42
+ feature?.dispose?.();
43
+ return feature;
44
+ }
45
+ installed.set(id, feature);
46
+ return feature;
47
+ });
48
+ shared.catch((error) => { if (!disposed) reportError(error, id); }).finally(() => pending.delete(id));
49
+ pending.set(id, shared);
50
+ }
51
+ if (!signal) return shared;
52
+ if (signal.aborted) return Promise.reject(signal.reason);
53
+ return new Promise((resolve, reject) => {
54
+ const onAbort = () => reject(signal.reason);
55
+ signal.addEventListener("abort", onAbort, { once: true });
56
+ shared.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
57
+ });
58
+ }
59
+
60
+ return {
61
+ prepare,
62
+ ensure,
63
+ has: (id) => installed.has(id),
64
+ dispose() {
65
+ disposed = true;
66
+ for (const feature of installed.values()) feature?.dispose?.();
67
+ installed.clear();
68
+ pending.clear();
69
+ prepared.clear();
70
+ },
71
+ };
72
+ }
@@ -0,0 +1,12 @@
1
+ import { BOARD_CSS } from "../board-style.js";
2
+ import { BoardView } from "../board-view.js";
3
+ import { cardPanel } from "../board-panel.js";
4
+
5
+ export function createFeature({ board = null, undo = null, redo = null } = {}) {
6
+ return {
7
+ id: "board",
8
+ nodeViews: board ? { board: (node, view, getPos) => new BoardView(node, view, getPos, { commands: board, undo, redo, panel: cardPanel(view) }) } : null,
9
+ css: board ? BOARD_CSS : null,
10
+ dispose() {},
11
+ };
12
+ }
@@ -0,0 +1,13 @@
1
+ // Code blocks: their language picker and the syntax colours. Loaded the first
2
+ // time a document has or takes a code block.
3
+ import { CodeBlockView, startHighlighter } from "../highlight.js";
4
+
5
+ export function createFeature({ view = null, highlight = {} } = {}) {
6
+ const highlighter = view ? startHighlighter(view, highlight) : null;
7
+ return {
8
+ id: "code",
9
+ nodeViews: { code_block: (node, v, getPos) => new CodeBlockView(node, v, getPos) },
10
+ css: null,
11
+ dispose() { highlighter?.destroy(); },
12
+ };
13
+ }