@panphora/clayjs 1.15.2 → 1.17.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 (551) 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 +84291 -57611
  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-ZUE7PSCI.js +1 -0
  21. package/dist/m/attrs/save-freeze-MYNVXREN.js +1 -0
  22. package/dist/m/c-2AZDZWCK.js +1 -0
  23. package/dist/m/c-2SPVQSHC.js +1 -0
  24. package/dist/m/c-2YYDMLNZ.js +1 -0
  25. package/dist/m/c-336SGU5W.js +1 -0
  26. package/dist/m/c-33JJ6CBX.js +2 -0
  27. package/dist/m/c-3GJ4SDE2.js +1 -0
  28. package/dist/m/c-3H6BZDNJ.js +6 -0
  29. package/dist/m/c-3OWDMNJR.js +2 -0
  30. package/dist/m/c-3RUU4TPX.js +91 -0
  31. package/dist/m/c-3XIRD4NU.js +1 -0
  32. package/dist/m/c-46XYM55L.js +1 -0
  33. package/dist/m/c-4DFEJ2PO.js +3 -0
  34. package/dist/m/c-4JZDZFF6.js +1 -0
  35. package/dist/m/c-4L3BTFRK.js +1 -0
  36. package/dist/m/c-4Q2XTQ2N.js +2 -0
  37. package/dist/m/c-4WKFKEKF.js +1 -0
  38. package/dist/m/c-56QRZTZH.js +1 -0
  39. package/dist/m/c-5J4E7O4Y.js +4 -0
  40. package/dist/m/c-5LKDWQTL.js +19 -0
  41. package/dist/m/c-5QWQ7A5L.js +1 -0
  42. package/dist/m/c-5RJFTLSF.js +1 -0
  43. package/dist/m/c-5V3MOVJT.js +1 -0
  44. package/dist/m/c-5VYOWDTI.js +1 -0
  45. package/dist/m/c-5YIHJ3TJ.js +1 -0
  46. package/dist/m/c-6AZOGAPE.js +1 -0
  47. package/dist/m/c-6CRUUXOX.js +5 -0
  48. package/dist/m/c-6HMB24EV.js +1 -0
  49. package/dist/m/c-6KEVEQ7L.js +1 -0
  50. package/dist/m/c-6NC5I2TV.js +1 -0
  51. package/dist/m/c-74MGYWZF.js +1 -0
  52. package/dist/m/c-7DJDKPGL.js +1 -0
  53. package/dist/m/c-7OLLJPWP.js +1 -0
  54. package/dist/m/c-7SWMYKAI.js +1 -0
  55. package/dist/m/c-A3E3NIMC.js +1 -0
  56. package/dist/m/c-A7LS5UZW.js +1 -0
  57. package/dist/m/c-AMSNPOCX.js +1 -0
  58. package/dist/m/c-AQLFEATB.js +1 -0
  59. package/dist/m/c-B3JM5ZXC.js +1 -0
  60. package/dist/m/c-BBVGTQG7.js +1 -0
  61. package/dist/m/c-BPF2NSPV.js +1 -0
  62. package/dist/m/c-BQ6LOBEE.js +1 -0
  63. package/dist/m/c-BTDI4Y23.js +1 -0
  64. package/dist/m/c-BWFVSBDF.js +1 -0
  65. package/dist/m/c-BYTKL6UX.js +1 -0
  66. package/dist/m/c-CFHW5AMY.js +6 -0
  67. package/dist/m/c-CJ3A7ZKE.js +1 -0
  68. package/dist/m/c-CQHPGKX2.js +1 -0
  69. package/dist/m/c-CYWLPPUS.js +1 -0
  70. package/dist/m/c-CZUSOB7V.js +1 -0
  71. package/dist/m/c-DCFSEJZG.js +1 -0
  72. package/dist/m/c-DCXOXBF3.js +1 -0
  73. package/dist/m/c-DFS4XZJV.js +2 -0
  74. package/dist/m/c-DNENWY4O.js +1 -0
  75. package/dist/m/c-DNZ5IFM6.js +1 -0
  76. package/dist/m/c-E4LIR5X5.js +1 -0
  77. package/dist/m/c-EJTGYSLN.js +1 -0
  78. package/dist/m/c-EVCQ3BKA.js +1 -0
  79. package/dist/m/c-EZU56NQV.js +34 -0
  80. package/dist/m/c-FDNHHZY5.js +1 -0
  81. package/dist/m/c-FILLTJA7.js +1 -0
  82. package/dist/m/c-FRLHSY7E.js +1 -0
  83. package/dist/m/c-FROJUZMV.js +1 -0
  84. package/dist/m/c-G2JPKCTP.js +1 -0
  85. package/dist/m/c-GB6TU7WV.js +1 -0
  86. package/dist/m/c-GFJJEBTU.js +1 -0
  87. package/dist/m/c-GJAEGGXY.js +1 -0
  88. package/dist/m/c-GOWBDM6J.js +1 -0
  89. package/dist/m/c-GQ6F6IDK.js +1 -0
  90. package/dist/m/c-GSFIGKOP.js +1 -0
  91. package/dist/m/c-GY422N3Q.js +1 -0
  92. package/dist/m/c-H4LG442Q.js +1 -0
  93. package/dist/m/c-HA6INT6J.js +1 -0
  94. package/dist/m/c-HAWFNBYG.js +1 -0
  95. package/dist/m/c-HDDEEL77.js +1 -0
  96. package/dist/m/c-HFKUVACH.js +1 -0
  97. package/dist/m/c-HHDFCNHV.js +1 -0
  98. package/dist/m/c-HPURYVZS.js +2 -0
  99. package/dist/m/c-I2ZATQCD.js +2 -0
  100. package/dist/m/c-IEM4NTCS.js +1 -0
  101. package/dist/m/c-IJUK2GPW.js +1 -0
  102. package/dist/m/c-IW6GGOU2.js +1 -0
  103. package/dist/m/c-JAX2667S.js +6 -0
  104. package/dist/m/c-JDCX26UP.js +25 -0
  105. package/dist/m/c-JRKY44DK.js +1 -0
  106. package/dist/m/c-JU3NK25H.js +1 -0
  107. package/dist/m/c-KAU6E43D.js +1 -0
  108. package/dist/m/c-KBS3UV6B.js +1 -0
  109. package/dist/m/c-KGVGROZT.js +1 -0
  110. package/dist/m/c-KI7M4AY3.js +1 -0
  111. package/dist/m/c-KIPQJX4B.js +1 -0
  112. package/dist/m/c-KQ6TYJAP.js +135 -0
  113. package/dist/m/c-KU3ZFADW.js +1 -0
  114. package/dist/m/c-L4WQSC27.js +1 -0
  115. package/dist/m/c-LFPHCXY3.js +1 -0
  116. package/dist/m/c-LRPUO2MX.js +1 -0
  117. package/dist/m/c-LWLBHLVC.js +3 -0
  118. package/dist/m/c-LZJPNR2W.js +1 -0
  119. package/dist/m/c-M2L47S2X.js +7 -0
  120. package/dist/m/c-MFP2ZWD4.js +1 -0
  121. package/dist/m/c-MKSQ4Y57.js +1 -0
  122. package/dist/m/c-MTQHWQC3.js +1 -0
  123. package/dist/m/c-N326R2JD.js +1 -0
  124. package/dist/m/c-N7JPUPGC.js +1 -0
  125. package/dist/m/c-NRPJEVJS.js +1 -0
  126. package/dist/m/c-NUFFJVVJ.js +1 -0
  127. package/dist/m/c-NUHP2XDB.js +1 -0
  128. package/dist/m/c-NUHSPT5P.js +1 -0
  129. package/dist/m/c-O72HVYSA.js +1 -0
  130. package/dist/m/c-OCSZ4QGN.js +1 -0
  131. package/dist/m/c-OIBUTDXI.js +1 -0
  132. package/dist/m/c-OIDRZKAF.js +1 -0
  133. package/dist/m/c-OW5QIGG3.js +1 -0
  134. package/dist/m/c-P7KNDG34.js +1 -0
  135. package/dist/m/c-PGRCK73F.js +1 -0
  136. package/dist/m/c-PQ5LK6QE.js +993 -0
  137. package/dist/m/c-PS5ARVWR.js +9 -0
  138. package/dist/m/c-PZ4XEPUP.js +2 -0
  139. package/dist/m/c-Q622NS2Y.js +1 -0
  140. package/dist/m/c-Q7NQD65V.js +1 -0
  141. package/dist/m/c-QH63LQOS.js +1 -0
  142. package/dist/m/c-QHIWXHRO.js +1 -0
  143. package/dist/m/c-QHWJ5ZIJ.js +1 -0
  144. package/dist/m/c-QICP2YR5.js +1 -0
  145. package/dist/m/c-QJ4OM3G6.js +1 -0
  146. package/dist/m/c-QNKREOL6.js +1 -0
  147. package/dist/m/c-R3L7ZJGB.js +2 -0
  148. package/dist/m/c-R7IWVINQ.js +1 -0
  149. package/dist/m/c-RM7J7BV2.js +1 -0
  150. package/dist/m/c-RSMOODDU.js +1 -0
  151. package/dist/m/c-RVMWGXEJ.js +1 -0
  152. package/dist/m/c-RYUXUUBX.js +1 -0
  153. package/dist/m/c-S4ZEH46B.js +9 -0
  154. package/dist/m/c-S7H2DRCB.js +1 -0
  155. package/dist/m/c-S7LK4KUE.js +1 -0
  156. package/dist/m/c-SDAXDM2D.js +1 -0
  157. package/dist/m/c-SGIA4IEQ.js +188 -0
  158. package/dist/m/c-SMBZG5X4.js +1 -0
  159. package/dist/m/c-SMGU7RT3.js +235 -0
  160. package/dist/m/c-SPIORRYS.js +1 -0
  161. package/dist/m/c-SWLRT66R.js +1 -0
  162. package/dist/m/c-SZGYW5HT.js +1 -0
  163. package/dist/m/c-TCCA5BZN.js +2 -0
  164. package/dist/m/c-TCJTVAFN.js +1 -0
  165. package/dist/m/c-TJ6WOJL3.js +67 -0
  166. package/dist/m/c-TQEMVJCS.js +1 -0
  167. package/dist/m/c-TRS6TM6O.js +1 -0
  168. package/dist/m/c-TYLXX4NF.js +21 -0
  169. package/dist/m/c-U62OB4BT.js +1 -0
  170. package/dist/m/c-U6YWUAR3.js +8 -0
  171. package/dist/m/c-UDCSU5G5.js +2 -0
  172. package/dist/m/c-UDUUJPFZ.js +1 -0
  173. package/dist/m/c-UGHH4MB3.js +1 -0
  174. package/dist/m/c-UL2YUY52.js +1 -0
  175. package/dist/m/c-VAK7SG37.js +1 -0
  176. package/dist/m/c-VDSS4K3Z.js +1 -0
  177. package/dist/m/c-VE6ZMYMC.js +1 -0
  178. package/dist/m/c-VG5XIN4T.js +1 -0
  179. package/dist/m/c-VHM5XQHK.js +1 -0
  180. package/dist/m/c-VHNSBD2M.js +1 -0
  181. package/dist/m/c-VI5RMSVY.js +1 -0
  182. package/dist/m/c-VKEDWYDF.js +1 -0
  183. package/dist/m/c-VRZN56XY.js +1 -0
  184. package/dist/m/c-W5MCHEGA.js +1 -0
  185. package/dist/m/c-WDC5GVKU.js +1 -0
  186. package/dist/m/c-WN5AY3OZ.js +1 -0
  187. package/dist/m/c-WNYBLIOZ.js +1 -0
  188. package/dist/m/c-WRBKEJD4.js +1 -0
  189. package/dist/m/c-WWXIDH4O.js +28 -0
  190. package/dist/m/c-WYFDLITL.js +1 -0
  191. package/dist/m/c-XCNTK76U.js +1 -0
  192. package/dist/m/c-XMS6DPBR.js +1 -0
  193. package/dist/m/c-XN6MBKI6.js +67 -0
  194. package/dist/m/c-XPELB6E5.js +1 -0
  195. package/dist/m/c-XQXZAO7N.js +1 -0
  196. package/dist/m/c-Y5HBBIQ4.js +1 -0
  197. package/dist/m/c-ZE46QDIH.js +1 -0
  198. package/dist/m/c-ZUCOKOHT.js +1 -0
  199. package/dist/m/c-ZVPVXTJF.js +4 -0
  200. package/dist/m/c-ZVRKXKM7.js +1 -0
  201. package/dist/m/c-ZWIOJGMT.js +1 -0
  202. package/dist/m/core/admin-attrs-QHOKPJ7W.js +1 -0
  203. package/dist/m/core/autosave-HRYGUWV6.js +1 -0
  204. package/dist/m/core/conflict-notice-6MROJN2L.js +1 -0
  205. package/dist/m/core/draft-mode-WSQAT67S.js +1 -0
  206. package/dist/m/core/host-permission-FNXVEFNE.js +1 -0
  207. package/dist/m/core/is-edit-mode-KTWA3O5N.js +1 -0
  208. package/dist/m/core/persist-JFYRHMRJ.js +3 -0
  209. package/dist/m/core/recovery-FLTCC2OC.js +1 -0
  210. package/dist/m/core/save-HQZ4S667.js +1 -0
  211. package/dist/m/core/save-core-KGQWFUNH.js +1 -0
  212. package/dist/m/core/snapshot-EGQZNT45.js +1 -0
  213. package/dist/m/core/unsaved-warning-IDJLD66H.js +1 -0
  214. package/dist/m/dom/all-APS5ZSKP.js +1 -0
  215. package/dist/m/dom/dom-helpers-L7OSR6S4.js +1 -0
  216. package/dist/m/events/index-RN2JNGBV.js +1 -0
  217. package/dist/m/internals/index-PVXGPUDA.js +1 -0
  218. package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
  219. package/dist/m/lib/region-policy-SB2AWVG5.js +1 -0
  220. package/dist/m/loader-S4I7GKXR.js +1 -0
  221. package/dist/m/manifest-4C419D67.js +1 -0
  222. package/dist/m/manifest-edit-B077F288.js +1 -0
  223. package/dist/m/manifest.json +3988 -0
  224. package/dist/m/options/options-EJMS3K24.js +1 -0
  225. package/dist/m/page/editor-NNJA4OKD.js +202 -0
  226. package/dist/m/plugins/ai-edit-QN4PMMNL.js +6 -0
  227. package/dist/m/plugins/bind-text-NX46N5UV.js +1 -0
  228. package/dist/m/plugins/cms-HP3LSTPN.js +2293 -0
  229. package/dist/m/plugins/demo-RM5IR6AO.js +1 -0
  230. package/dist/m/plugins/derive-UAAA4V4S.js +1 -0
  231. package/dist/m/plugins/indicator-URDBBOQO.js +1 -0
  232. package/dist/m/plugins/live-HFWD4OIH.js +1 -0
  233. package/dist/m/plugins/menu-3HIEQSI6.js +138 -0
  234. package/dist/m/plugins/message-LQ6EDH4V.js +1 -0
  235. package/dist/m/plugins/page-JOKB6XPR.js +2 -0
  236. package/dist/m/plugins/page-view-NXOJHNW6.js +1 -0
  237. package/dist/m/plugins/people-SGPJKANK.js +1 -0
  238. package/dist/m/plugins/quickcrop-QNY3PKLD.js +26 -0
  239. package/dist/m/plugins/quickcrop-QNY3PKLD.js.LEGAL.txt +7 -0
  240. package/dist/m/plugins/richclay-NRQDSWX6.js +517 -0
  241. package/dist/m/plugins/richclay-NRQDSWX6.js.LEGAL.txt +1 -0
  242. package/dist/m/plugins/sortable-GWT4PWDV.js +1 -0
  243. package/dist/m/plugins/source-4ZRSAIYM.js +1 -0
  244. package/dist/m/plugins/undo-VDA5P7FH.js +1 -0
  245. package/dist/m/plugins/unsent-HJFLQJ2O.js +1 -0
  246. package/dist/m/plugins/upgrade-W7FG5VFG.js +1 -0
  247. package/dist/m/plugins/upload-WLYKK6TL.js +1 -0
  248. package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +21 -0
  249. package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +1 -0
  250. package/dist/m/plugins/wire-4N6GYAF6.js +1 -0
  251. package/dist/m/reader-core-DXYCURFF.js +68 -0
  252. package/dist/m/route/route-KOCZSEGN.js +1 -0
  253. package/dist/m/sync/live-sync-J7VMYTKS.js +1 -0
  254. package/dist/m/ui/index-3JLVI27O.js +1 -0
  255. package/dist/m/utils/index-4TJCVLT7.js +1 -0
  256. package/dist/m/vendor/Sortable.vendor-A755ITLN.js +3 -0
  257. package/dist/m/vendor/Sortable.vendor-A755ITLN.js.LEGAL.txt +1 -0
  258. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +6 -0
  259. package/dist/offline-manifest.json +4034 -0
  260. package/dist/page/themes/light-1.css +430 -0
  261. package/dist/server/core.cjs +23748 -22855
  262. package/dist/server/offline-runtime.cjs +4954 -0
  263. package/dist/server/offline-runtime.mjs +4923 -0
  264. package/dist/server/rooms.mjs +9 -0
  265. package/docs/guide.md +1078 -669
  266. package/entries/all.js +2 -0
  267. package/entries/clay-data.js +1 -1
  268. package/entries/clay-dom.js +2 -0
  269. package/entries/clay-events.js +2 -0
  270. package/entries/clay-internals.js +2 -0
  271. package/entries/clay-options.js +2 -0
  272. package/entries/clay-router.js +2 -0
  273. package/entries/clay-ui.js +2 -0
  274. package/entries/clay-utils.js +2 -0
  275. package/entries/clay.js +2 -0
  276. package/entries/sap.js +6 -6
  277. package/fonts/ibm-plex-mono-LICENSE +93 -0
  278. package/fonts/ibm-plex-mono-latin-300-normal.woff2 +0 -0
  279. package/fonts/ibm-plex-mono-latin-400-italic.woff2 +0 -0
  280. package/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  281. package/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  282. package/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  283. package/fonts/ibm-plex-sans-LICENSE +93 -0
  284. package/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  285. package/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  286. package/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  287. package/fonts/ibm-plex.css +71 -0
  288. package/fonts/inter-LICENSE +93 -0
  289. package/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  290. package/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  291. package/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  292. package/fonts/inter-greek-wght-normal.woff2 +0 -0
  293. package/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  294. package/fonts/inter-latin-wght-normal.woff2 +0 -0
  295. package/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  296. package/fonts/inter.css +62 -0
  297. package/fonts/newsreader-LICENSE +93 -0
  298. package/fonts/newsreader-latin-ext-opsz-normal.woff2 +0 -0
  299. package/fonts/newsreader-latin-opsz-normal.woff2 +0 -0
  300. package/fonts/newsreader-vietnamese-opsz-normal.woff2 +0 -0
  301. package/fonts/newsreader.css +26 -0
  302. package/fonts/work-sans-LICENSE +93 -0
  303. package/fonts/work-sans-latin-wght-normal.woff2 +0 -0
  304. package/fonts/work-sans.css +8 -0
  305. package/package.json +20 -3
  306. package/packed-contract.json +1 -0
  307. package/src/attrs/add-from.js +27 -4
  308. package/src/attrs/save-freeze.js +7 -52
  309. package/src/core/admin-attrs.js +119 -12
  310. package/src/core/admin-contenteditable.js +12 -10
  311. package/src/core/admin-inputs.js +14 -15
  312. package/src/core/admin-onclick.js +2 -0
  313. package/src/core/autosave.js +74 -21
  314. package/src/core/boot-phase.js +2 -1
  315. package/src/core/conflict-download.js +19 -23
  316. package/src/core/conflict-notice-ui.js +1173 -0
  317. package/src/core/conflict-notice.js +140 -697
  318. package/src/core/download.js +32 -0
  319. package/src/core/edit-mode.js +147 -14
  320. package/src/core/etag.js +30 -12
  321. package/src/core/host-meta.js +90 -8
  322. package/src/core/host-permission.js +29 -0
  323. package/src/core/is-edit-mode.js +145 -12
  324. package/src/core/mode-key.js +125 -0
  325. package/src/core/mode-lifecycle.js +544 -0
  326. package/src/core/mode-receipt.js +30 -0
  327. package/src/core/mode-refusal.js +13 -0
  328. package/src/core/mode-resources.js +52 -0
  329. package/src/core/mode-support.js +82 -0
  330. package/src/core/mode-switch.js +259 -0
  331. package/src/core/mode-transition.js +112 -0
  332. package/src/core/page-data-write.js +305 -0
  333. package/src/core/page-data.js +26 -227
  334. package/src/core/page-format-wire.js +4 -0
  335. package/src/core/page-format.js +58 -0
  336. package/src/core/persist.js +37 -9
  337. package/src/core/preserve-authored.js +114 -0
  338. package/src/core/recovery.js +772 -0
  339. package/src/core/runtime-url.js +105 -0
  340. package/src/core/save-core.js +113 -12
  341. package/src/core/save-replace.js +72 -0
  342. package/src/core/save-size-notice.js +134 -0
  343. package/src/core/save-size.js +140 -0
  344. package/src/core/save.js +806 -119
  345. package/src/core/snapshot.js +159 -11
  346. package/src/core/source-map.js +157 -9
  347. package/src/core/unsaved-warning.js +14 -4
  348. package/src/core/upgrade-run.js +408 -0
  349. package/src/core/version.js +18 -0
  350. package/src/lib/assets.js +30 -0
  351. package/src/lib/autosave-debug-gate.js +50 -0
  352. package/src/lib/autosave-scheduler.js +140 -0
  353. package/src/lib/boot-imports.js +8 -0
  354. package/src/lib/derived.js +3 -1
  355. package/src/lib/dirty-gate.js +15 -0
  356. package/src/lib/doc-storage.js +33 -0
  357. package/src/lib/effect.js +9 -4
  358. package/src/lib/mutation.js +4 -2
  359. package/src/lib/offline-runtime.js +226 -0
  360. package/src/lib/preload.js +48 -0
  361. package/src/lib/root-attrs.js +1 -1
  362. package/src/lib/trash.js +18 -0
  363. package/src/lib/undo/capture-move.js +152 -0
  364. package/src/live/local-channel.js +61 -0
  365. package/src/live/local-lane.js +60 -9
  366. package/src/live/server-lane.js +6 -3
  367. package/src/loader-logic.js +76 -15
  368. package/src/loader.js +218 -30
  369. package/src/page/adopt-boot.js +76 -22
  370. package/src/page/adopt.js +25 -4
  371. package/src/page/awareness.js +230 -0
  372. package/src/page/blocks.js +113 -6
  373. package/src/page/blocksel.js +20 -4
  374. package/src/page/board-face.js +13 -0
  375. package/src/page/board-panel.js +5 -17
  376. package/src/page/board-style.js +2 -13
  377. package/src/page/board-view.js +1 -11
  378. package/src/page/board-views.js +3 -0
  379. package/src/page/board.js +2 -2
  380. package/src/page/boot-pack.js +26 -0
  381. package/src/page/boot.js +237 -0
  382. package/src/page/chart-tables.js +11 -0
  383. package/src/page/chart.js +6 -10
  384. package/src/page/checklist.js +1 -1
  385. package/src/page/checkpoint-proof.js +289 -0
  386. package/src/page/chrome.js +431 -63
  387. package/src/page/csv-import.js +17 -2
  388. package/src/page/document-title-dom.js +52 -0
  389. package/src/page/document-title-runtime.js +90 -0
  390. package/src/page/document-title.js +12 -0
  391. package/src/page/drag.js +48 -13
  392. package/src/page/drop.js +2 -1
  393. package/src/page/editor.js +399 -90
  394. package/src/page/emoji-data.js +162 -0
  395. package/src/page/feature-loader.js +72 -0
  396. package/src/page/features/board.js +21 -0
  397. package/src/page/features/chart.js +11 -0
  398. package/src/page/features/code.js +13 -0
  399. package/src/page/features/dates.js +180 -0
  400. package/src/page/features/emoji.js +187 -0
  401. package/src/page/features/media.js +290 -0
  402. package/src/page/features/mentions.js +188 -0
  403. package/src/page/features/registry.js +28 -0
  404. package/src/page/features/table.js +383 -0
  405. package/src/page/{tool-view.js → features/tool.js} +33 -2
  406. package/src/page/format-codec.js +215 -0
  407. package/src/page/format-command.js +9 -0
  408. package/src/page/format-gate.js +179 -0
  409. package/src/page/format-host.js +54 -0
  410. package/src/page/format-level.js +157 -0
  411. package/src/page/format-native-commands.js +142 -0
  412. package/src/page/format-native.js +42 -0
  413. package/src/page/highlight-slot.js +23 -0
  414. package/src/page/highlight.js +30 -16
  415. package/src/page/history-intent.js +78 -0
  416. package/src/page/inline.js +45 -28
  417. package/src/page/items.js +67 -14
  418. package/src/page/link.js +1 -0
  419. package/src/page/markdown.js +112 -3
  420. package/src/page/media-views.js +3 -0
  421. package/src/page/media.js +33 -245
  422. package/src/page/mention-picker.js +99 -0
  423. package/src/page/merge.js +1 -1
  424. package/src/page/outline-cache.js +11 -0
  425. package/src/page/placeholder.js +27 -7
  426. package/src/page/projection.js +240 -51
  427. package/src/page/reader-patch.js +306 -0
  428. package/src/page/recover.js +1 -1
  429. package/src/page/rich/page-tool.js +1 -4
  430. package/src/page/runtime-owner.js +27 -0
  431. package/src/page/save-projection.js +46 -4
  432. package/src/page/save-states.js +1 -1
  433. package/src/page/schema.js +47 -0
  434. package/src/page/table-rect-key.js +3 -0
  435. package/src/page/table-select.js +4 -3
  436. package/src/page/table-style.js +1 -1
  437. package/src/page/table-views.js +3 -0
  438. package/src/page/table.js +8 -293
  439. package/src/page/themes/content.css +25 -1
  440. package/src/page/themes/light-1.css +163 -2
  441. package/src/page/themes/light-1.source.css +269 -0
  442. package/src/page/tool-capability.js +3 -0
  443. package/src/page/tool-views.js +3 -0
  444. package/src/page/view-hooks.js +32 -0
  445. package/src/page/view.js +390 -107
  446. package/src/page/ybind/awareness.js +172 -0
  447. package/src/page/ybind/binding.js +40 -5
  448. package/src/page/ybind/bridge.js +50 -47
  449. package/src/page/ybind/decode.js +52 -19
  450. package/src/page/ybind/doc.js +27 -2
  451. package/src/page/ybind/group.js +12 -3
  452. package/src/page/ybind/index.js +2 -0
  453. package/src/page/ybind/lineage.js +71 -0
  454. package/src/page/ybind/marks.js +26 -1
  455. package/src/page/ybind/migrate.js +48 -13
  456. package/src/page/ybind/moves.js +32 -21
  457. package/src/page/ybind/positions.js +50 -0
  458. package/src/page/ybind/provider.js +97 -24
  459. package/src/page/ybind/recovery-proof.js +191 -0
  460. package/src/page/ybind/recovery-replay.js +488 -0
  461. package/src/page/ybind/tokens.js +15 -6
  462. package/src/page/ybind/walk.js +10 -3
  463. package/src/page/ybind/writer.js +48 -4
  464. package/src/plugins/ai-edit.js +167 -32
  465. package/src/plugins/bind-text.js +218 -0
  466. package/src/plugins/cms.js +93 -0
  467. package/src/plugins/indicator.js +57 -5
  468. package/src/plugins/live.js +319 -35
  469. package/src/plugins/menu/claygoo.svg.js +1 -0
  470. package/src/plugins/menu/seo-head.js +323 -0
  471. package/src/plugins/menu/seo.js +849 -0
  472. package/src/plugins/menu.js +425 -0
  473. package/src/plugins/page-view.js +131 -0
  474. package/src/plugins/page.js +1131 -270
  475. package/src/plugins/people.js +158 -31
  476. package/src/plugins/richclay.js +119 -9
  477. package/src/plugins/sortable.js +370 -60
  478. package/src/plugins/source.js +189 -366
  479. package/src/plugins/undo.js +289 -19
  480. package/src/plugins/unsent.js +132 -0
  481. package/src/plugins/upgrade-run.js +1 -0
  482. package/src/plugins/upgrade.js +210 -0
  483. package/src/plugins/upload-task.js +571 -0
  484. package/src/plugins/upload-ui.js +265 -0
  485. package/src/plugins/upload.js +34 -153
  486. package/src/plugins/visitor-drafts/inert.js +32 -0
  487. package/src/plugins/visitor-drafts/intro.js +2 -1
  488. package/src/plugins/visitor-drafts/owner-chip.js +3 -0
  489. package/src/plugins/visitor-drafts/prompt.js +2 -2
  490. package/src/plugins/visitor-drafts-owner.js +85 -0
  491. package/src/plugins/visitor-drafts.js +31 -42
  492. package/src/plugins/wire.js +82 -1
  493. package/src/reader-core.js +26 -0
  494. package/src/recovery/conflicts.js +287 -0
  495. package/src/recovery/identity.js +154 -0
  496. package/src/recovery/store.js +792 -0
  497. package/src/recovery/undo.js +257 -0
  498. package/src/server/checkpoint/index.js +68 -4
  499. package/src/server/offline-runtime/index.js +214 -0
  500. package/src/server/rooms/index.js +2 -1
  501. package/src/server/rooms/live-data-rooms.js +2 -2
  502. package/src/server/rooms/page-admission.js +33 -0
  503. package/src/server/rooms/page-lineage.js +56 -0
  504. package/src/server/rooms/page-rebase.js +11 -5
  505. package/src/server/rooms/page-state.js +2 -0
  506. package/src/server/rooms/page-write-admission.js +9 -3
  507. package/src/source/session.js +705 -0
  508. package/src/sync/conflict-revert.js +5 -4
  509. package/src/sync/conflicts-edit.js +119 -0
  510. package/src/sync/conflicts-facade.js +49 -0
  511. package/src/sync/conflicts.js +99 -1
  512. package/src/sync/editing-marker.js +37 -6
  513. package/src/sync/editing-sender.js +8 -1
  514. package/src/sync/live-sync-edit.js +63 -0
  515. package/src/sync/live-sync.js +364 -119
  516. package/src/sync/local-presence.js +187 -0
  517. package/src/sync/merge-declarations.js +10 -0
  518. package/src/sync/merge-engine.js +12 -0
  519. package/src/sync/presence-store.js +412 -0
  520. package/src/sync/presence-target.js +243 -0
  521. package/src/sync/presence.js +72 -41
  522. package/src/sync/saved-handoff.js +200 -0
  523. package/src/sync/saved-stream.js +544 -0
  524. package/src/sync/version-guard.js +92 -0
  525. package/src/sync/wire-profile.js +81 -0
  526. package/src/ui/bevel-controls.js +2 -8
  527. package/src/ui/page-scheme.js +7 -0
  528. package/src/ui/skins/cms.js +1 -1
  529. package/src/ui/theme-config.js +150 -0
  530. package/src/ui/theme.js +6 -138
  531. package/src/ui/toast-lazy.js +11 -0
  532. package/src/vendor/LICENSES-optional.md +253 -0
  533. package/src/vendor/highlight.vendor.js +3 -0
  534. package/src/vendor/hljs-bash.vendor.js +2 -0
  535. package/src/vendor/hljs-css.vendor.js +2 -0
  536. package/src/vendor/hljs-javascript.vendor.js +2 -0
  537. package/src/vendor/hljs-json.vendor.js +2 -0
  538. package/src/vendor/hljs-python.vendor.js +2 -0
  539. package/src/vendor/hljs-xml.vendor.js +2 -0
  540. package/src/vendor/hyper-html-api-data.vendor.js +1 -1
  541. package/src/vendor/hyper-html-api-upgrade.vendor.js +5 -0
  542. package/src/vendor/hyper-morph-identity.vendor.js +2 -0
  543. package/src/vendor/hyper-morph-json.vendor.js +3 -0
  544. package/src/vendor/hyper-morph.vendor.js +6 -6
  545. package/src/vendor/hyper-undo.vendor.js +1 -1
  546. package/src/vendor/hypercms.vendor.js +169 -84
  547. package/src/vendor/marked.vendor.js +68 -0
  548. package/src/vendor/overtype.vendor.js +994 -0
  549. package/src/vendor/richclay.vendor.js +17 -17
  550. package/src/vendor/sap-core.vendor.js +5 -5
  551. package/src/vendor/sap-ownership.vendor.js +11 -7
@@ -0,0 +1,78 @@
1
+ // Typing joins one undo item the way it does in other editors: Mod-z removes the last
2
+ // word typed, not the last letter. Only the editor's own input path marks a
3
+ // transaction; a command or a programmatic dispatch stays one undo item by itself.
4
+ import { state } from "../vendor/prosemirror.vendor.js";
5
+ import { STEP } from "./blocks.js";
6
+
7
+ export const HISTORY_INTENT = "page-history-intent";
8
+ export const GROUP_MS = 500;
9
+
10
+ const blockIdAt = (doc, pos) => {
11
+ const $pos = doc.resolve(pos);
12
+ return $pos.depth ? $pos.node(1).attrs.id : null;
13
+ };
14
+
15
+ const markKey = (st) => (st.storedMarks ?? st.selection.$from.marks()).map((m) => `${m.type.name}:${JSON.stringify(m.attrs)}`).sort().join(",");
16
+
17
+ export function typingIntent(st, from, to, text) {
18
+ return { kind: "insert", blockId: blockIdAt(st.doc, from), from, end: from + text.length, replaced: from !== to, text, marks: markKey(st), at: Date.now() };
19
+ }
20
+
21
+ export function continuesGroup(prev, next) {
22
+ if (!prev || !next || prev.kind !== next.kind) return false;
23
+ if (next.kind === "compose") return prev.id === next.id;
24
+ if (prev.blockId !== next.blockId || next.at - prev.at > GROUP_MS) return false;
25
+ if (next.kind === "insert") return !next.replaced && next.from === prev.end && next.marks === prev.marks && !(/\s$/.test(prev.text) && /^\S/.test(next.text));
26
+ return next.direction === prev.direction && (next.direction < 0 ? next.to === prev.from : next.from === prev.from);
27
+ }
28
+
29
+ const DELETES = { deleteContentBackward: -1, deleteContentForward: 1 };
30
+ const pendingDeletes = new WeakMap();
31
+ const PENDING_MS = 1000;
32
+ export const DERIVED = "page-derived";
33
+
34
+ // A native deletion arrives as a DOM change with no handler, sometimes after a delayed flush
35
+ // (Android waits 20 ms), so the beforeinput that announced it is held with the document it saw
36
+ // and claimed by the next document change, whatever that change turns out to be.
37
+ export function takePendingDelete(view) {
38
+ const mark = pendingDeletes.get(view) ?? null;
39
+ pendingDeletes.delete(view);
40
+ return mark;
41
+ }
42
+
43
+ export function deletionIntent(tr, mark) {
44
+ if (!mark || tr.before !== mark.doc || Date.now() - mark.at > PENDING_MS || tr.getMeta(STEP) || tr.steps.length !== 1) return null;
45
+ const step = tr.steps[0];
46
+ if (step.slice?.size !== 0 || step.from === step.to) return null;
47
+ const $from = tr.before.resolve(step.from), $to = tr.before.resolve(step.to);
48
+ if (!$from.parent.isTextblock || !$from.sameParent($to)) return null;
49
+ return { kind: "delete", blockId: $from.depth ? $from.node(1).attrs.id : null, direction: mark.direction, from: step.from, to: step.to, at: mark.at };
50
+ }
51
+
52
+ export function intentOf(transactions, view = null) {
53
+ const pending = view ? takePendingDelete(view) : null;
54
+ const primary = transactions.filter((t) => !t.getMeta(DERIVED));
55
+ if (primary.length !== 1) return null;
56
+ const tr = primary[0];
57
+ const composition = tr.getMeta("composition");
58
+ if (composition != null) return { kind: "compose", id: composition, at: Date.now() };
59
+ return tr.getMeta(HISTORY_INTENT) ?? deletionIntent(tr, pending);
60
+ }
61
+
62
+ export function historyIntentPlugin() {
63
+ return new state.Plugin({
64
+ props: {
65
+ handleTextInput(view, from, to, text, deflt) {
66
+ view.dispatch(deflt().setMeta(HISTORY_INTENT, typingIntent(view.state, from, to, text)));
67
+ return true;
68
+ },
69
+ handleDOMEvents: {
70
+ beforeinput(view, event) {
71
+ const direction = DELETES[event.inputType];
72
+ if (direction) pendingDeletes.set(view, { direction, at: Date.now(), doc: view.state.doc });
73
+ return false;
74
+ },
75
+ },
76
+ },
77
+ });
78
+ }
@@ -12,37 +12,43 @@
12
12
  // "@" and the node's JSON. No mark key starts with "@", so a U+FFFC typed as
13
13
  // text stays text. Private-use characters typed as text need nothing here:
14
14
  // encodeForMerge (replay.js) gives each one a code of its own.
15
+ // A mark this schema has no type for keeps its token or named descriptor
16
+ // instead of throwing (format-codec.js): the unit's key is written back exactly
17
+ // as it was read. A mark of this schema with no compact code of its own is
18
+ // written as a named descriptor too, x[name, attrs], so a new mark never takes a
19
+ // letter an older writer already reads.
15
20
  import { model } from "../vendor/prosemirror.vendor.js";
21
+ import { descriptors, entriesOf, isOpaque, keyOf, opaqueMark } from "./format-codec.js";
22
+ import { nativeAttrs } from "./format-native.js";
16
23
 
17
24
  export const ATOM = "";
18
25
  export const CODES = { strong: "b", em: "i", strike: "s", code: "c", link: "a" };
26
+ const NATIVE = new Set(["underline", "text_color", "highlight"]);
19
27
  const NAMES = Object.fromEntries(Object.entries(CODES).map(([name, code]) => [code, name]));
20
28
 
21
29
  export function markKey(marks) {
22
30
  let key = "";
31
+ const named = [];
23
32
  for (const mark of marks) {
33
+ if (isOpaque(mark)) {
34
+ key += keyOf(mark.attrs.entries);
35
+ continue;
36
+ }
24
37
  const code = CODES[mark.type.name];
25
- if (!code) throw new TypeError(`page: the mark ${mark.type.name} has no key code`);
38
+ if (!code) {
39
+ // A mark with no compact letter travels as a named descriptor, after the codes
40
+ // and in the codec's order: one of the four formats only with attrs it writes,
41
+ // any other (a mention, a date) with its own attrs.
42
+ const native = nativeAttrs(mark.type.name, mark.attrs);
43
+ if (!native && NATIVE.has(mark.type.name)) throw new TypeError(`page: the mark ${mark.type.name} has no key code`);
44
+ named.push({ name: mark.type.name, attrs: native ?? mark.attrs });
45
+ continue;
46
+ }
26
47
  key += code;
27
48
  const names = Object.keys(mark.type.attrs);
28
49
  if (names.length) key += JSON.stringify(names.map((name) => mark.attrs[name]));
29
50
  }
30
- return key;
31
- }
32
-
33
- // The end of the JSON array that starts at s[i].
34
- function arrayEnd(s, i) {
35
- let depth = 0, str = false;
36
- for (; i < s.length; i++) {
37
- const ch = s[i];
38
- if (str) {
39
- if (ch === "\\") i++;
40
- else if (ch === '"') str = false;
41
- } else if (ch === '"') str = true;
42
- else if (ch === "[") depth++;
43
- else if (ch === "]" && --depth === 0) return i + 1;
44
- }
45
- throw new SyntaxError(`page: unterminated attrs in mark key ${s}`);
51
+ return named.length ? key + keyOf(named) : key;
46
52
  }
47
53
 
48
54
  const parsed = new WeakMap();
@@ -54,19 +60,30 @@ export function marksOf(schema, key) {
54
60
  let marks = cache.get(key);
55
61
  if (marks) return marks;
56
62
  marks = [];
57
- for (let i = 0; i < key.length;) {
58
- const type = schema.marks[NAMES[key[i]]];
59
- if (!type) throw new TypeError(`page: unknown mark code ${key[i]} in ${key}`);
60
- i++;
61
- let attrs = null;
62
- if (key[i] === "[") {
63
- const end = arrayEnd(key, i);
64
- const values = JSON.parse(key.slice(i, end));
65
- attrs = Object.fromEntries(Object.keys(type.attrs).map((name, j) => [name, values[j]]));
66
- i = end;
63
+ const opaque = [];
64
+ for (const desc of descriptors(key)) {
65
+ // A named descriptor this schema does have a type for is that mark, not an opaque
66
+ // entry: one of the four formats only with attrs it writes, any other mark only
67
+ // with attributes its type declares. Anything else stays opaque, so nothing is lost.
68
+ if (desc.code === undefined) {
69
+ const native = nativeAttrs(desc.name, desc.attrs);
70
+ const type = schema.marks[desc.name];
71
+ if (native) marks = type.create(native).addToSet(marks);
72
+ else if (type && !NATIVE.has(desc.name) && Object.keys(desc.attrs ?? {}).every((field) => Object.hasOwn(type.attrs, field))) marks = type.create(desc.attrs).addToSet(marks);
73
+ else opaque.push(desc);
74
+ continue;
75
+ }
76
+ const name = NAMES[desc.code];
77
+ if (!name) {
78
+ opaque.push(desc);
79
+ continue;
67
80
  }
68
- marks = type.create(attrs).addToSet(marks);
81
+ const coded = schema.marks[name];
82
+ if (!coded) throw new TypeError(`page: unknown mark code ${desc.code} in ${key}`);
83
+ const attrs = desc.attrs ? Object.fromEntries(Object.keys(coded.attrs).map((field, j) => [field, desc.attrs[j]])) : null;
84
+ marks = coded.create(attrs).addToSet(marks);
69
85
  }
86
+ if (opaque.length) marks = opaqueMark(entriesOf(opaque)).addToSet(marks);
70
87
  cache.set(key, marks);
71
88
  return marks;
72
89
  }
package/src/page/items.js CHANGED
@@ -5,13 +5,15 @@
5
5
  import { state } from "../vendor/prosemirror.vendor.js";
6
6
  import { schema } from "./schema.js";
7
7
  import { isKey, keyBetween, newId } from "./model.js";
8
+ import { isOpaque, opaqueMark } from "./format-codec.js";
8
9
  import { outline, subtreeRange } from "./blocks.js";
9
10
  import { asStep } from "./blocksel.js";
10
11
  import { pickFiles, hostPlace } from "./media.js";
11
12
  import { makeTable, insertTableNode } from "./table.js";
12
13
  import { pickCsv } from "./csv-import.js";
13
14
  import { makeBoard, insertBoardNode } from "./board.js";
14
- import { chartFromSlash, chartTables } from "./chart.js";
15
+ import { chartTables } from "./chart-tables.js";
16
+ import { viewFeatures } from "./view-hooks.js";
15
17
 
16
18
  const { TextSelection } = state;
17
19
  const N = schema.nodes;
@@ -26,6 +28,7 @@ export const BLOCK_TYPES = [
26
28
  { key: "check", label: "Checklist", description: "Tasks to tick off", icon: "☐", type: "list_item", attrs: { kind: "check" }, hint: "[]", aliases: ["todo", "to-do", "task", "check", "checkbox"] },
27
29
  { key: "toggle", label: "Toggle", description: "Hide details under a line", icon: "▸", type: "toggle", attrs: { open: true }, hint: ">", aliases: ["collapse", "details", "fold"] },
28
30
  { key: "callout", label: "Callout", description: "Make a note stand out", icon: "!", type: "callout", aliases: ["note", "aside", "info"] },
31
+ { key: "quote", label: "Quote", description: "Set text apart as a quotation", icon: "\u201c", type: "quote", hint: "\"", aliases: ["blockquote", "quotation", "cite"] },
29
32
  { key: "code", label: "Code", description: "Code with highlighting", icon: "</>", type: "code_block", hint: "```", aliases: ["pre", "snippet"] },
30
33
  ];
31
34
 
@@ -40,6 +43,10 @@ export const INSERT_ITEMS = [
40
43
 
41
44
  export const CHART_ITEM = { key: "chart", label: "Chart", description: "A bar or line chart from a table", icon: "📊", aliases: ["graph", "plot", "bar", "line"], insert: "chart" };
42
45
 
46
+ // The chart module and its view part load together, before the block draws: a
47
+ // document that never takes a chart never fetches either.
48
+ export const loadChartPart = (view) => (viewFeatures.get(view)?.ensure("chart") ?? Promise.resolve()).catch(() => {}).then(() => import("./chart.js"));
49
+
43
50
  export const MENU_ITEMS = [...BLOCK_TYPES, ...INSERT_ITEMS];
44
51
 
45
52
  export const insertItemsFor = (st, items = MENU_ITEMS) => (chartTables(st.doc).length ? [...items, CHART_ITEM] : items);
@@ -67,8 +74,29 @@ export function retype(tr, index, item) {
67
74
  return tr.setBlockType(at + 1, at + node.nodeSize - 1, N[item.type], { ...keep, ...(item.attrs || {}) });
68
75
  }
69
76
 
77
+ // A command this runtime cannot carry out on the content it was given: the
78
+ // document and the clipboard keep what they hold.
79
+ export const REFUSED = Object.freeze({ status: "refused", code: "unsupported-format" });
80
+
81
+ const opaqueType = opaqueMark([]).type;
82
+ const holdsOpaque = (node) => {
83
+ let found = false;
84
+ node.forEach((child) => { if (!found && child.marks.some(isOpaque)) found = true; });
85
+ return found;
86
+ };
87
+
88
+ // A block that is not a textblock cannot be turned into anything, and one this
89
+ // runtime cannot rewrite, or whose opaque marks the target type would erase, is
90
+ // refused rather than converted into something that lost them.
91
+ export function canTurnInto(node, item) {
92
+ if (!node.isTextblock || node.type === N.opaque_block) return false;
93
+ return !holdsOpaque(node) || N[item.type].allowsMarkType(opaqueType);
94
+ }
95
+
70
96
  export function turnInto(st, index, item) {
71
- return st.doc.child(index).isTextblock ? retype(st.tr, index, item) : null;
97
+ const node = st.doc.child(index);
98
+ if (!node.isTextblock) return null;
99
+ return canTurnInto(node, item) ? retype(st.tr, index, item) : REFUSED;
72
100
  }
73
101
 
74
102
  // A new empty paragraph after the block at `index` and its subtree, at the
@@ -116,37 +144,62 @@ export function insertDivider(st, index, tr = st.tr) {
116
144
  return tr.setSelection(TextSelection.create(tr.doc, to + 2));
117
145
  }
118
146
 
147
+ // The skipped letters between a query and a candidate it can be read out of,
148
+ // or Infinity when it cannot. Only queries of 2 to 32 letters count, so a single
149
+ // letter never drags in half the menu.
150
+ function subsequenceCost(query, candidate) {
151
+ if (query.length < 2 || query.length > 32) return Infinity;
152
+ let at = -1;
153
+ let cost = 0;
154
+ for (const ch of query) {
155
+ const next = candidate.indexOf(ch, at + 1);
156
+ if (next < 0) return Infinity;
157
+ cost += next - at - 1;
158
+ at = next;
159
+ }
160
+ return cost;
161
+ }
162
+
119
163
  // Exact name or alias first, then label prefix, word or alias prefix,
120
- // substring; equal ranks keep menu order. No fuzzy matching.
164
+ // substring, description word, and last a fuzzy subsequence of the label, key
165
+ // or aliases. Equal tiers keep the lower cost, then menu order.
121
166
  export function rankItems(items, query) {
122
167
  const q = query.trim().toLowerCase();
123
168
  if (!q) return items;
124
169
  const rank = (it) => {
125
170
  const label = it.label.toLowerCase();
126
171
  const names = [it.key, ...(it.aliases || []), ...(it.hint ? [it.hint] : [])].map((s) => s.toLowerCase());
127
- if (label === q || names.includes(q)) return 0;
128
- if (label.startsWith(q)) return 1;
129
- if ([...label.split(/\s+/), ...names].some((s) => s.startsWith(q))) return 2;
130
- if ([label, ...names].some((s) => s.includes(q))) return 3;
131
- if (q.length >= 3 && it.description && it.description.toLowerCase().split(/[^a-z0-9]+/).some((w) => w.startsWith(q))) return 4;
132
- return Infinity;
172
+ if (label === q || names.includes(q)) return [0, 0];
173
+ if (label.startsWith(q)) return [1, 0];
174
+ if ([...label.split(/\s+/), ...names].some((s) => s.startsWith(q))) return [2, 0];
175
+ if ([label, ...names].some((s) => s.includes(q))) return [3, 0];
176
+ if (q.length >= 3 && it.description && it.description.toLowerCase().split(/[^a-z0-9]+/).some((w) => w.startsWith(q))) return [4, 0];
177
+ const cost = Math.min(...[label, it.key, ...(it.aliases || [])].map((s) => subsequenceCost(q, s.toLowerCase())));
178
+ return cost === Infinity ? null : [5, cost];
133
179
  };
134
- return items.map((it, order) => ({ it, order, rank: rank(it) })).filter((x) => x.rank !== Infinity).sort((a, b) => a.rank - b.rank || a.order - b.order).map((x) => x.it);
180
+ return items.map((it, order) => ({ it, order, rank: rank(it) })).filter((x) => x.rank).sort((a, b) => a.rank[0] - b.rank[0] || a.rank[1] - b.rank[1] || a.order - b.order).map((x) => x.it);
135
181
  }
136
182
 
137
183
  // What a pick does in the insert menu for the block at `index`. A divider and
138
184
  // the file pickers have their own placement; a block type retypes an empty line
139
- // and otherwise goes into a new block below the line's whole subtree.
185
+ // and otherwise goes into a new block below the line's whole subtree. A block
186
+ // type from a later format asks the format gate before it writes anything.
140
187
  export function runItem(view, item, index, remove = null) {
188
+ if (item.type !== "quote") return insertItem(view, item, index, remove);
189
+ return import("./format-native-commands.js").then(({ runFormat }) => runFormat(view, () => insertItem(view, item, index, remove)));
190
+ }
191
+
192
+ function insertItem(view, item, index, remove = null) {
141
193
  const st = view.state;
142
194
  if (item.insert === "image" || item.insert === "file") return pickFiles(view, item.insert === "image" ? "image/*" : "", hostPlace(st, index), remove);
143
195
  if (item.insert === "csv") return pickCsv(view, remove);
144
- if (item.insert === "table") return insertTableNode(st, view.dispatch, makeTable(), remove);
145
- if (item.insert === "board") return insertBoardNode(st, view.dispatch, makeBoard(), remove);
146
- if (item.insert === "chart") return chartFromSlash(view, index, remove);
196
+ if (item.insert === "table") return insertTableNode(st, view.dispatch, makeTable(), remove, null, index);
197
+ if (item.insert === "board") return insertBoardNode(st, view.dispatch, makeBoard(), remove, index);
198
+ if (item.insert === "chart") return loadChartPart(view).then(({ chartFromSlash }) => chartFromSlash(view, index, remove));
147
199
  let tr = st.tr;
148
200
  if (remove) tr.delete(remove.from, remove.to);
149
201
  const node = tr.doc.child(index);
202
+ if (node.isTextblock && node.content.size === 0 && !canTurnInto(node, item)) return REFUSED;
150
203
  if (item.insert === "divider") tr = insertDivider(st, index, tr);
151
204
  else if (node.isTextblock && node.content.size === 0) tr = retype(tr, index, item);
152
205
  else {
package/src/page/link.js CHANGED
@@ -79,6 +79,7 @@ class LinkPopover {
79
79
  this.edit.addEventListener("click", () => this.openEdit());
80
80
  this.remove.addEventListener("click", () => this.apply(""));
81
81
  this.input.addEventListener("keydown", (e) => {
82
+ if (e.isComposing || e.keyCode === 229) return;
82
83
  if (e.key === "Enter") { e.preventDefault(); this.apply(this.input.value); }
83
84
  else if (e.key === "Escape") { e.preventDefault(); this.close(); }
84
85
  });
@@ -8,6 +8,7 @@ import { retype, replaceWithDivider } from "./items.js";
8
8
  import { asStep } from "./blocksel.js";
9
9
  import { insertTableNode } from "./table.js";
10
10
  import { tableFromGrid } from "./table-paste.js";
11
+ import { safeHref } from "./address.js";
11
12
 
12
13
  const { InputRule } = inputrules;
13
14
  const N = schema.nodes;
@@ -29,6 +30,87 @@ const dividerRule = new InputRule(/^---$/, (st, match, start, end) => {
29
30
  return asStep(replaceWithDivider(st.tr, $s.index(0)));
30
31
  });
31
32
 
33
+ // Inline shortcuts typed around text: **bold** and __bold__ to strong, *italic*
34
+ // and _italic_ to em, `code`, ~~strike~~, and [label](address) to a link. The
35
+ // delimiters are deleted and the mark is added over the body, which stays where
36
+ // it is, so the marks it already carries survive. Backspace right after puts the
37
+ // delimiter characters back through the input rule's own undo.
38
+ const OPENS = "\\s\\(\\[\\{'\u2018\u201C\u0022";
39
+ const OPENS_ONE = new RegExp(`[${OPENS}]`);
40
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
41
+ const bodyRe = (exclude) => `([^\\s${exclude}](?:[\\s\\S]*?[^\\s${exclude}])?)`;
42
+
43
+ export const composing = (event, view) => !!event?.isComposing || event?.keyCode === 229 || !!view?.composing;
44
+
45
+ const trailingBackslashes = (text) => {
46
+ let n = 0;
47
+ for (let i = text.length - 1; i >= 0 && text[i] === "\\"; i--) n++;
48
+ return n;
49
+ };
50
+
51
+ // An opening delimiter needs the start of a textblock or whitespace and opening
52
+ // punctuation before it, and neither delimiter may be escaped.
53
+ const opensAt = (st, at) => {
54
+ const $at = st.doc.resolve(at);
55
+ if (!$at.parent.inlineContent) return false;
56
+ if (trailingBackslashes($at.parent.textBetween(0, $at.parentOffset, null, "\uFFFC")) % 2) return false;
57
+ return $at.parentOffset === 0 || OPENS_ONE.test($at.parent.textBetween($at.parentOffset - 1, $at.parentOffset));
58
+ };
59
+
60
+ const inline = (st, match, start, end, markOf) => {
61
+ const mark = markOf(match);
62
+ if (!mark || !st.selection.empty) return null;
63
+ if (start < 0 || start > end || end > st.doc.content.size) return null;
64
+ const $start = st.doc.resolve(start), $end = st.doc.resolve(end);
65
+ if (!$start.sameParent($end) || !$start.parent.inlineContent) return null;
66
+ const [all, prefix, body, close] = match;
67
+ if (inlineCode(st, start, end)) return null;
68
+ const open = all.slice(prefix.length, all.length - body.length - close.length);
69
+ const from = start + prefix.length;
70
+ if (!opensAt(st, from)) return null;
71
+ if (trailingBackslashes(all.slice(0, all.length - close.length)) % 2) return null;
72
+ const pending = all.length - (end - start);
73
+ if (pending < 0) return null;
74
+ const tr = st.tr;
75
+ if (pending) tr.insertText(all.slice(-pending), end, end);
76
+ const bodyStart = from + open.length;
77
+ const bodyEnd = bodyStart + body.length;
78
+ tr.delete(bodyEnd, start + all.length);
79
+ tr.delete(from, bodyStart);
80
+ tr.addMark(from, from + body.length, mark);
81
+ tr.removeStoredMark(mark.type);
82
+ return asStep(tr);
83
+ };
84
+
85
+ const inlineRule = (re, markOf) => new InputRule(
86
+ re,
87
+ (st, match, start, end) => inline(st, match, start, end, markOf),
88
+ { inCodeMark: false },
89
+ );
90
+
91
+ const markRule = (open, close, type) => inlineRule(
92
+ new RegExp(`(^|[${OPENS}])${escapeRe(open)}${bodyRe(escapeRe(close))}(${escapeRe(close)})$`),
93
+ () => type.create(),
94
+ );
95
+
96
+ const linkRule = inlineRule(
97
+ new RegExp(`(^|[${OPENS}])\\[${bodyRe("\\[\\]")}(\\]\\([^()\\s]*\\))$`),
98
+ (match) => {
99
+ const href = safeHref(match[3].slice(2, -1));
100
+ return href ? schema.marks.link.create({ href }) : null;
101
+ },
102
+ );
103
+
104
+ const INLINE_RULES = [
105
+ markRule("**", "**", schema.marks.strong),
106
+ markRule("__", "__", schema.marks.strong),
107
+ markRule("*", "*", schema.marks.em),
108
+ markRule("_", "_", schema.marks.em),
109
+ markRule("`", "`", schema.marks.code),
110
+ markRule("~~", "~~", schema.marks.strike),
111
+ linkRule,
112
+ ];
113
+
32
114
  const LISTS = ["paragraph", "list_item"];
33
115
 
34
116
  export const MARKDOWN_RULES = [
@@ -39,9 +121,34 @@ export const MARKDOWN_RULES = [
39
121
  blockRule(/^>\s$/, { type: "toggle", attrs: { open: true } }),
40
122
  blockRule(/^```$/, { type: "code_block" }, ["paragraph"], true),
41
123
  dividerRule,
124
+ ...INLINE_RULES,
42
125
  ];
43
126
 
44
- export const markdownPlugin = () => inputrules.inputRules({ rules: MARKDOWN_RULES });
127
+ // The shortest shortcut that quotes a line: `" ` typed at the start of a
128
+ // paragraph. It writes a floor-1 block, so it asks the format gate first and
129
+ // converts only when the gate answered applied; the transaction it dispatches is
130
+ // the input-rule one, recorded on this plugin so Backspace puts the literal `" `
131
+ // back and undo keeps the conversion whole. The typed space is never swallowed:
132
+ // when the format cannot be written, the space is inserted as itself.
133
+ function quoteShortcut(plugin, view, from, to, text) {
134
+ if (text !== " " || from !== to) return false;
135
+ const st = view.state;
136
+ const $from = st.doc.resolve(from);
137
+ const node = $from.parent;
138
+ if ($from.depth !== 1 || node.type !== N.paragraph || $from.parentOffset !== 1 || node.textContent !== '"') return false;
139
+ if (!view.editable || typeof view.runFormatCommand !== "function") return false;
140
+ const plain = () => { if (!view.isDestroyed) view.dispatch(st.tr.insertText(" ", from, to)); };
141
+ const at = $from.before(1);
142
+ import("./format-native-commands.js").then(({ quoteFromSpace }) => quoteFromSpace(plugin, view, { at, from, to }, plain), plain);
143
+ return true;
144
+ }
145
+
146
+ export const markdownPlugin = () => {
147
+ const plugin = inputrules.inputRules({ rules: MARKDOWN_RULES });
148
+ const rules = plugin.props.handleTextInput;
149
+ plugin.props.handleTextInput = (view, from, to, text) => quoteShortcut(plugin, view, from, to, text) || rules(view, from, to, text);
150
+ return plugin;
151
+ };
45
152
 
46
153
  // "| Name | Owner |" then Enter at the end of a paragraph: the paragraph becomes
47
154
  // a table with those column names and one empty row, the caret in its first cell.
@@ -59,13 +166,15 @@ export function tableFromMarkdownRow(st, dispatch) {
59
166
  return insertTableNode(st, dispatch, table, { from: $from.start(), to: $from.end() });
60
167
  }
61
168
 
62
- export const markdownKeys = () => keymap.keymap({ Enter: tableFromMarkdownRow });
169
+ export const markdownKeys = () => keymap.keymap({
170
+ Enter: (st, dispatch, view) => !composing(null, view) && tableFromMarkdownRow(st, dispatch),
171
+ });
63
172
 
64
173
  export function backspaceToParagraph(st, dispatch) {
65
174
  const { $from, empty } = st.selection;
66
175
  if (!empty || $from.depth !== 1 || $from.parentOffset !== 0) return false;
67
176
  const n = $from.parent;
68
- if (!(n.type === N.heading || n.type === N.callout || (n.type === N.code_block && n.content.size === 0))) return false;
177
+ if (!(n.type === N.heading || n.type === N.callout || n.type === N.quote || (n.type === N.code_block && n.content.size === 0))) return false;
69
178
  if (dispatch) dispatch(asStep(st.tr.setNodeMarkup($from.before(), N.paragraph, { id: n.attrs.id, pos: n.attrs.pos, parent: n.attrs.parent })));
70
179
  return true;
71
180
  }
@@ -0,0 +1,3 @@
1
+ // The bookmark renderer a media plugin can reach, filled by the media view part
2
+ // when it loads, per editor. An upload still tracks and settles with no renderer.
3
+ export const mediaViews = new WeakMap();