@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
@@ -0,0 +1,408 @@
1
+ import { replace as replaceDocument } from "./save-replace.js";
2
+ import { lastSeenEtag } from "./etag.js";
3
+ import { claimDocumentOperation, releaseDocumentOperation } from "./save-core.js";
4
+ import { isNewerVersion } from "./version.js";
5
+ import { upgrade, readMeta } from "../vendor/hyper-html-api-upgrade.vendor.js";
6
+
7
+ const MANIFEST_FAILED = "The new version could not be downloaded. Nothing was changed.";
8
+ const SAVE_FAILED = "The upgrade could not be saved. Nothing was changed.";
9
+ const CONFLICT = "This document changed somewhere else. Reload and try again.";
10
+ const BUSY = "Something else is changing this document. Nothing was changed.";
11
+
12
+ async function digestOf(bytes) {
13
+ const subtle = globalThis.crypto && globalThis.crypto.subtle;
14
+ if (!subtle) return null;
15
+ const digest = await subtle.digest("SHA-256", bytes);
16
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
17
+ }
18
+
19
+ async function fetchOrNull(fetchFn, url) {
20
+ try {
21
+ const res = await fetchFn(url, { credentials: "omit" });
22
+ if (!res || !res.ok) return null;
23
+ return res;
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ function manifestUrl(update, doc, loc) {
30
+ if (update && typeof update.latestUrl === "string" && update.latestUrl) return update.latestUrl;
31
+ const source = readMeta(doc, "hyper-source");
32
+ if (!source) return null;
33
+ try {
34
+ const url = new URL(source, loc ? loc.href : undefined);
35
+ url.pathname = url.pathname.replace(/\.html$/, ".json");
36
+ return url.href;
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ function templateOf(doc) {
43
+ const value = readMeta(doc, "clay-template");
44
+ if (!value) return null;
45
+ const [id, variant] = value.split(/\s+/);
46
+ return id && variant ? { id, variant } : null;
47
+ }
48
+
49
+ function leavesOf(value, path = "", out = []) {
50
+ if (Array.isArray(value)) {
51
+ for (const item of value) leavesOf(item, `${path}[]`, out);
52
+ return out;
53
+ }
54
+ if (value && typeof value === "object") {
55
+ for (const [key, item] of Object.entries(value)) leavesOf(item, path ? `${path}.${key}` : key, out);
56
+ return out;
57
+ }
58
+ if (value !== null && value !== undefined) out.push({ path, value });
59
+ return out;
60
+ }
61
+
62
+ function valuesAt(data, pattern) {
63
+ let current = [data];
64
+ for (const part of String(pattern).split(".")) {
65
+ const list = part.endsWith("[]");
66
+ const key = list ? part.slice(0, -2) : part;
67
+ const next = [];
68
+ for (const node of current) {
69
+ const value = key ? node?.[key] : node;
70
+ if (list) {
71
+ if (Array.isArray(value)) next.push(...value);
72
+ } else {
73
+ next.push(value);
74
+ }
75
+ }
76
+ current = next;
77
+ }
78
+ return current;
79
+ }
80
+
81
+ export function lostValues(before, after, renames = {}) {
82
+ const kept = new Map();
83
+ for (const { path, value } of leavesOf(after)) {
84
+ const key = `${path}\u0000${String(value)}`;
85
+ kept.set(key, (kept.get(key) || 0) + 1);
86
+ }
87
+ const lost = [];
88
+ for (const { path, value } of leavesOf(before)) {
89
+ const key = `${path}\u0000${String(value)}`;
90
+ if (kept.get(key) > 0) {
91
+ kept.set(key, kept.get(key) - 1);
92
+ continue;
93
+ }
94
+ const renamed = renames[path];
95
+ if (typeof renamed === "string" && valuesAt(after, renamed).some((candidate) => candidate === value)) continue;
96
+ lost.push({ path, value });
97
+ }
98
+ return lost;
99
+ }
100
+
101
+ async function evaluateTransform(code) {
102
+ const blob = new Blob([code], { type: "text/javascript" });
103
+ const url = URL.createObjectURL(blob);
104
+ try {
105
+ return await new Function("url", "return import(url)")(url);
106
+ } finally {
107
+ URL.revokeObjectURL(url);
108
+ }
109
+ }
110
+
111
+ async function declaredRenames(htmlText) {
112
+ const sourceDoc = new DOMParser().parseFromString(htmlText, "text/html");
113
+ const tag = sourceDoc.querySelector('script[type="text/hyper-upgrade"]');
114
+ if (!tag) return {};
115
+ const mod = await evaluateTransform(tag.textContent || "");
116
+ return mod && mod.renames && typeof mod.renames === "object" ? mod.renames : {};
117
+ }
118
+
119
+ export async function prepare(update, {
120
+ doc = document,
121
+ loc = location,
122
+ fetchFn = (url, init) => fetch(url, init),
123
+ } = {}) {
124
+ const clay = (typeof window !== "undefined" && window.clay) || {};
125
+ if (clay.isDraftMode) return { ok: false, code: "draft" };
126
+ if (!update || typeof update.version !== "string") return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
127
+
128
+ const latestUrl = manifestUrl(update, doc, loc);
129
+ if (!latestUrl) return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
130
+
131
+ const manifestRes = await fetchOrNull(fetchFn, latestUrl);
132
+ if (!manifestRes) return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
133
+ let latest;
134
+ try {
135
+ latest = await manifestRes.json();
136
+ } catch {
137
+ return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
138
+ }
139
+ if (!latest || typeof latest !== "object") return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
140
+
141
+ const template = templateOf(doc);
142
+ if (!template || latest.id !== template.id || latest.variant !== template.variant) {
143
+ return { ok: false, code: "template", message: "This upgrade is for a different document." };
144
+ }
145
+ const fromVersion = readMeta(doc, "hyper-version");
146
+ if (!fromVersion || typeof latest.version !== "string" || !isNewerVersion(latest.version, fromVersion)) {
147
+ return { ok: false, code: "not-newer", message: "There is no newer version to install." };
148
+ }
149
+ if (typeof latest.html !== "string" || !latest.html) return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
150
+
151
+ let htmlUrl;
152
+ try {
153
+ htmlUrl = new URL(latest.html, latestUrl).href;
154
+ } catch {
155
+ return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
156
+ }
157
+ if (!/^https:\/\/hyperclay\.com\/templates\//.test(htmlUrl)) {
158
+ return { ok: false, code: "source", message: "This upgrade is not from the template host." };
159
+ }
160
+
161
+ const htmlRes = await fetchOrNull(fetchFn, htmlUrl);
162
+ if (!htmlRes) return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
163
+ let htmlText;
164
+ try {
165
+ htmlText = await htmlRes.text();
166
+ } catch {
167
+ return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
168
+ }
169
+ const digest = await digestOf(new TextEncoder().encode(htmlText));
170
+ if (!digest || digest !== String(latest.sha256 || "").toLowerCase()) {
171
+ return { ok: false, code: "unavailable", message: MANIFEST_FAILED };
172
+ }
173
+
174
+ let result;
175
+ let renames;
176
+ try {
177
+ renames = await declaredRenames(htmlText);
178
+ result = await upgrade.run({
179
+ sourceUrl: htmlUrl,
180
+ doc,
181
+ loc,
182
+ fromVersion,
183
+ fetchFn: async () => ({ ok: true, status: 200, text: async () => htmlText }),
184
+ });
185
+ } catch (err) {
186
+ return { ok: false, code: "transform", message: err && err.message ? err.message : MANIFEST_FAILED };
187
+ }
188
+ const afterDoc = new DOMParser().parseFromString(result.html, "text/html");
189
+ const before = upgrade.extractAll(doc).dataByName;
190
+ const after = upgrade.extractAll(afterDoc).dataByName;
191
+ const lost = lostValues(before, after, renames);
192
+ const discarded = result.summary && result.summary.totals ? result.summary.totals.discarded || 0 : 0;
193
+ if (lost.length || discarded > 0) {
194
+ return { ok: false, code: "lost", lost, discarded, message: lossMessage(lost, discarded) };
195
+ }
196
+ return { ok: true, html: result.html, version: result.toVersion || latest.version };
197
+ }
198
+
199
+ function lossMessage(lost, discarded) {
200
+ const names = lost.slice(0, 5).map(({ path }) => path);
201
+ const more = lost.length > names.length ? ` and ${lost.length - names.length} more` : "";
202
+ if (names.length) return `This upgrade would lose data: ${names.join(", ")}${more}. Nothing was changed.`;
203
+ return `This upgrade would lose ${discarded} value${discarded === 1 ? "" : "s"}. Nothing was changed.`;
204
+ }
205
+
206
+ function flushFailure(err) {
207
+ const state = err && err.state;
208
+ if (state === "conflict") return { ok: false, code: "conflict", message: CONFLICT };
209
+ const reason = err && err.message ? err.message : "The document could not be saved.";
210
+ return { ok: false, code: "flush", state: state || "failed", message: reason };
211
+ }
212
+
213
+ export async function apply(update, {
214
+ doc = document,
215
+ loc = location,
216
+ fetchFn = (url, init) => fetch(url, init),
217
+ clay = typeof window !== "undefined" ? window.clay : null,
218
+ reload = () => window.location.reload(),
219
+ replace = null,
220
+ etag,
221
+ } = {}) {
222
+ const pageClay = clay || {};
223
+ if (pageClay.isDraftMode) return { ok: false, code: "draft" };
224
+
225
+ // An upgrade flushes this generation and then replaces it with another, so it speaks
226
+ // for the whole document while it runs: a mode switch cannot start its barrier in the
227
+ // middle of one, and two upgrades cannot interleave their flushes and writes.
228
+ const operation = claimDocumentOperation("upgrade");
229
+ if (!operation) return { ok: false, code: "busy", message: BUSY };
230
+ try {
231
+ return await applyUnderSlot(update, { doc, loc, fetchFn, clay, reload, replace, etag });
232
+ } finally {
233
+ releaseDocumentOperation(operation);
234
+ }
235
+ }
236
+
237
+ async function applyUnderSlot(update, { doc, loc, fetchFn, clay, reload, replace, etag }) {
238
+ const pageClay = clay || {};
239
+
240
+ if (pageClay.isEditMode && pageClay.save && typeof pageClay.save.flush === "function") {
241
+ let flushed;
242
+ try {
243
+ flushed = await pageClay.save.flush();
244
+ } catch (err) {
245
+ return flushFailure(err);
246
+ }
247
+ if (flushed && typeof flushed.state === "string" && flushed.state !== "saved" && flushed.state !== "clean" && flushed.state !== "view") {
248
+ return { ok: false, code: "flush", state: flushed.state, message: "This document has changes that could not be saved. Nothing was changed." };
249
+ }
250
+ }
251
+
252
+ const prepared = await prepare(update, { doc, loc, fetchFn });
253
+ if (!prepared.ok) return prepared;
254
+
255
+ const save = typeof replace === "function"
256
+ ? replace
257
+ : pageClay.save && typeof pageClay.save.replace === "function"
258
+ ? pageClay.save.replace
259
+ : replaceDocument;
260
+ let result;
261
+ try {
262
+ result = await save(prepared.html, { etag: etag !== undefined ? etag : lastSeenEtag() });
263
+ } catch {
264
+ return { ok: false, code: "failed", message: SAVE_FAILED };
265
+ }
266
+ if (!result || !result.ok) {
267
+ const code = result && result.code ? result.code : "failed";
268
+ if (code === "conflict") return { ok: false, code, message: CONFLICT };
269
+ if (code === "forbidden") return { ok: false, code, message: "You are not allowed to save this document." };
270
+ if (code === "unsupported") return { ok: false, code, message: "This host cannot save the upgrade." };
271
+ return { ok: false, code, message: SAVE_FAILED };
272
+ }
273
+ reload();
274
+ return { ok: true, html: prepared.html, version: prepared.version, etag: result.etag };
275
+ }
276
+
277
+ function element(doc, tag, props = {}) {
278
+ const node = doc.createElement(tag);
279
+ node.setAttribute("clay", "editor-ui");
280
+ for (const [key, value] of Object.entries(props)) {
281
+ if (key === "style") node.setAttribute("style", value);
282
+ else if (key === "text") node.textContent = value;
283
+ else node.setAttribute(key, value);
284
+ }
285
+ return node;
286
+ }
287
+
288
+ function downloadName(loc) {
289
+ const last = (loc && loc.pathname ? loc.pathname : "/").split("/").filter(Boolean).pop();
290
+ let name = last || "page.html";
291
+ try {
292
+ name = decodeURIComponent(name);
293
+ } catch {}
294
+ return /\.html?$/i.test(name) ? name : `${name}.html`;
295
+ }
296
+
297
+ export function downloadCopy({ clay, doc = document, loc = location } = {}) {
298
+ const getHTML = clay && clay.getHTML;
299
+ if (typeof getHTML !== "function" || typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return false;
300
+ try {
301
+ const url = URL.createObjectURL(new Blob([getHTML()], { type: "text/html" }));
302
+ const a = doc.createElement("a");
303
+ a.setAttribute("clay", "editor-ui");
304
+ a.href = url;
305
+ a.download = downloadName(loc);
306
+ a.style.setProperty("display", "none", "important");
307
+ doc.body.appendChild(a);
308
+ a.click();
309
+ a.remove();
310
+ setTimeout(() => URL.revokeObjectURL(url), 40000);
311
+ return true;
312
+ } catch {
313
+ return false;
314
+ }
315
+ }
316
+
317
+ async function hasHistory(clay) {
318
+ const host = clay && clay.host;
319
+ if (!host || !host.versions) return false;
320
+ try {
321
+ return (await host.versions) === true;
322
+ } catch {
323
+ return false;
324
+ }
325
+ }
326
+
327
+ export async function start(update, options = {}) {
328
+ const doc = options.doc || document;
329
+ const clay = options.clay || (typeof window !== "undefined" ? window.clay : null) || {};
330
+ if (clay.isDraftMode) return null;
331
+ if (!update || typeof update.version !== "string") return null;
332
+
333
+ const versions = await hasHistory(clay);
334
+ const root = element(doc, "div", {
335
+ role: "dialog",
336
+ "aria-modal": "true",
337
+ "data-clay-upgrade": "",
338
+ style: "position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:16px;background:rgba(15,17,21,.42);font-family:system-ui,-apple-system,'Segoe UI',sans-serif;",
339
+ });
340
+ const panel = element(doc, "div", {
341
+ style: "width:min(460px,100%);padding:20px;border-radius:14px;background:var(--clay-ui-bg,#fff);color:var(--clay-ui-fg,#1f2328);box-shadow:0 18px 48px -16px rgba(0,0,0,.55);font-size:14px;line-height:1.5;",
342
+ });
343
+ const title = element(doc, "h2", { text: `Upgrade to version ${update.version}`, style: "margin:0 0 8px;font-size:16px;" });
344
+ const body = element(doc, "p", {
345
+ text: `Your data moves into version ${update.version}. The page's layout and code are replaced; your current version stays in this document's history.`,
346
+ style: "margin:0 0 16px;",
347
+ });
348
+ const status = element(doc, "p", { role: "status", style: "margin:0 0 12px;min-height:1.5em;" });
349
+ const actions = element(doc, "div", { style: "display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;" });
350
+ const buttonStyle = "padding:7px 12px;border-radius:8px;border:1px solid rgba(23,28,36,.2);background:transparent;color:inherit;font:inherit;cursor:pointer;";
351
+ const cancel = element(doc, "button", { type: "button", text: "Cancel", style: buttonStyle });
352
+ const download = versions ? null : element(doc, "button", { type: "button", text: "Download a copy first", style: buttonStyle });
353
+ const agree = element(doc, "button", {
354
+ type: "button",
355
+ text: "Upgrade",
356
+ style: `${buttonStyle}border-color:transparent;background:#2f6fed;color:#fff;font-weight:600;`,
357
+ });
358
+ if (!versions) agree.disabled = true;
359
+
360
+ if (download) actions.append(download);
361
+ actions.append(cancel, agree);
362
+ panel.append(title, body, status, actions);
363
+ root.append(panel);
364
+ doc.body.appendChild(root);
365
+
366
+ let closed = false;
367
+ const close = () => {
368
+ if (closed) return;
369
+ closed = true;
370
+ root.remove();
371
+ };
372
+
373
+ if (download) {
374
+ download.addEventListener("click", () => {
375
+ if (downloadCopy({ clay, doc, loc: options.loc })) {
376
+ agree.disabled = false;
377
+ status.textContent = "Your copy was downloaded.";
378
+ } else {
379
+ status.textContent = "The copy could not be downloaded. Nothing was changed.";
380
+ }
381
+ });
382
+ }
383
+
384
+ cancel.addEventListener("click", close);
385
+
386
+ let resolveDone;
387
+ const done = new Promise((resolve) => {
388
+ resolveDone = resolve;
389
+ });
390
+
391
+ agree.addEventListener("click", async () => {
392
+ agree.disabled = true;
393
+ if (download) download.disabled = true;
394
+ status.textContent = `Upgrading to version ${update.version}…`;
395
+ const result = await apply(update, options);
396
+ if (result.ok) {
397
+ close();
398
+ resolveDone(result);
399
+ return;
400
+ }
401
+ status.textContent = result.message || SAVE_FAILED;
402
+ agree.disabled = false;
403
+ if (download) download.disabled = false;
404
+ resolveDone(result);
405
+ });
406
+
407
+ return { root, title, body, status, cancel, download, upgrade: agree, close, done };
408
+ }
@@ -0,0 +1,18 @@
1
+ export function parseVersion(value) {
2
+ if (typeof value !== "string") return null;
3
+ const trimmed = value.trim().replace(/^v/, "");
4
+ return /^\d+(\.\d+)*$/.test(trimmed) ? trimmed.split(".").map(Number) : null;
5
+ }
6
+
7
+ export function isNewerVersion(candidate, current) {
8
+ const next = parseVersion(candidate);
9
+ const now = parseVersion(current);
10
+ if (!next || !now) return false;
11
+ const length = Math.max(next.length, now.length);
12
+ for (let index = 0; index < length; index++) {
13
+ const candidatePart = next[index] || 0;
14
+ const currentPart = now[index] || 0;
15
+ if (candidatePart !== currentPart) return candidatePart > currentPart;
16
+ }
17
+ return false;
18
+ }
@@ -0,0 +1,30 @@
1
+ // Libraries a document asks for by name, loaded the first time it asks. Each is a module in this
2
+ // release, so whichever host serves ClayJS serves them too, offline included.
3
+ const ASSETS = {
4
+ overtype: () => import("../vendor/overtype.vendor.js"),
5
+ marked: () => import("../vendor/marked.vendor.js"),
6
+ highlight: () => import("../vendor/highlight.vendor.js"),
7
+ "highlight/bash": () => import("../vendor/hljs-bash.vendor.js"),
8
+ "highlight/css": () => import("../vendor/hljs-css.vendor.js"),
9
+ "highlight/javascript": () => import("../vendor/hljs-javascript.vendor.js"),
10
+ "highlight/json": () => import("../vendor/hljs-json.vendor.js"),
11
+ "highlight/python": () => import("../vendor/hljs-python.vendor.js"),
12
+ "highlight/xml": () => import("../vendor/hljs-xml.vendor.js"),
13
+ };
14
+
15
+ export function createAssets(table) {
16
+ const loading = new Map();
17
+ function load(name) {
18
+ if (!Object.hasOwn(table, name)) return Promise.reject(new Error(`clay.assets: no asset named "${name}"`));
19
+ if (!loading.has(name)) {
20
+ loading.set(name, table[name]().catch((error) => {
21
+ loading.delete(name);
22
+ throw error;
23
+ }));
24
+ }
25
+ return loading.get(name);
26
+ }
27
+ return { load, names: () => Object.keys(table) };
28
+ }
29
+
30
+ export const assets = createAssets(ASSETS);
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Autosave Debug Gate
3
+ *
4
+ * The boot-size half of the autosave debug utility: a person who never turns
5
+ * `clay:debug:autosave` on downloads nothing but this file. Each wrapper returns
6
+ * before the dynamic import when debug is off, and forwards to the real logger
7
+ * once it lands when it is on.
8
+ */
9
+
10
+ const DEBUG_KEY = 'clay:debug:autosave';
11
+
12
+ export function isDebugEnabled() {
13
+ try {
14
+ return localStorage.getItem(DEBUG_KEY) === 'true';
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ function load() {
21
+ return import('./autosave-debug.js').catch((err) => {
22
+ console.warn('[autosave-debug] Failed to load the debug logger:', err);
23
+ return null;
24
+ });
25
+ }
26
+
27
+ export function logSaveCheck(label, matches) {
28
+ if (!isDebugEnabled()) return;
29
+ load().then((mod) => mod?.logSaveCheck(label, matches));
30
+ }
31
+
32
+ export function logBaseline(event, details = '') {
33
+ if (!isDebugEnabled()) return;
34
+ load().then((mod) => mod?.logBaseline(event, details));
35
+ }
36
+
37
+ export function logUnloadDiff(currentContents, lastSaved) {
38
+ if (!isDebugEnabled()) return;
39
+ load().then((mod) => mod?.logUnloadDiff(currentContents, lastSaved));
40
+ }
41
+
42
+ export function logUnloadDiffSync(currentContents, lastSaved) {
43
+ if (!isDebugEnabled()) return;
44
+ load().then((mod) => mod?.logUnloadDiffSync(currentContents, lastSaved));
45
+ }
46
+
47
+ export function preloadIfEnabled() {
48
+ if (!isDebugEnabled()) return;
49
+ load().then((mod) => mod?.preloadIfEnabled());
50
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Automatic-save scheduling.
3
+ *
4
+ * One automatic attempt at a time, and none until this module admits it: the idle
5
+ * and maximum-wait state that used to live in the Mutation hub's debounce is here,
6
+ * so nothing captures the page for a save that is still only an intention.
7
+ *
8
+ * The clock, the timers and the attempt are injected, so the cadence is executable
9
+ * with fake time and testable without a DOM.
10
+ */
11
+
12
+ export const IDLE_MS = 1500;
13
+ export const MIN_GAP_MS = 1200;
14
+ export const CONTINUOUS_MS = 10000;
15
+ export const MAX_COOLDOWN_MS = 30000;
16
+
17
+ // Adaptive spacing: make the gap after a save at least COST_MULTIPLIER times that
18
+ // save's synchronous cost, so a three-second save earns twelve seconds of quiet
19
+ // rather than spending the quiet window doing the next capture. Off for now, and
20
+ // the multiplier is zero while it is off, which makes the cooldown term exactly
21
+ // MIN_GAP_MS and keeps today's 1,500 / 10,000 ms cadence.
22
+ export const ADAPTIVE_SPACING = false;
23
+ export const COST_MULTIPLIER = ADAPTIVE_SPACING ? 4 : 0;
24
+
25
+ // "No such time yet": before the first activity or the first completed save the
26
+ // page must be governed by its own timers, not by when this module was imported.
27
+ const NO_TIME = Number.NEGATIVE_INFINITY;
28
+
29
+ /**
30
+ * The earliest moment the next automatic attempt may be admitted.
31
+ *
32
+ * A quiet period can flush after IDLE_MS even when the continuous-typing ceiling
33
+ * is longer, and a save's own completion is never treated as an idle period: quiet
34
+ * time starts no earlier than the save finished.
35
+ */
36
+ export function nextAutoAt({ firstDirtyAt = NO_TIME, lastActivityAt = NO_TIME, finishedAt = NO_TIME, costMs = 0 } = {}) {
37
+ const first = firstDirtyAt ?? NO_TIME;
38
+ const last = lastActivityAt ?? NO_TIME;
39
+ const finished = finishedAt ?? NO_TIME;
40
+ const cooldown = Math.min(MAX_COOLDOWN_MS, Math.max(MIN_GAP_MS, COST_MULTIPLIER * costMs));
41
+ const quietAt = Math.max(last, finished) + IDLE_MS;
42
+ const continuousAt = Math.max(first + CONTINUOUS_MS, finished + cooldown);
43
+ return Math.max(finished + MIN_GAP_MS, Math.min(quietAt, continuousAt));
44
+ }
45
+
46
+ export function createAutosaveScheduler({
47
+ now = () => Date.now(),
48
+ setTimer = (fn, ms) => setTimeout(fn, ms),
49
+ clearTimer = (id) => clearTimeout(id),
50
+ run = () => {}
51
+ } = {}) {
52
+ let firstDirtyAt = null;
53
+ let lastActivityAt = null;
54
+ let finishedAt = NO_TIME;
55
+ let costMs = 0;
56
+ let timer = null;
57
+ let timerAt = null;
58
+ let pending = false;
59
+ let running = false;
60
+
61
+ function dueAt() {
62
+ return nextAutoAt({ firstDirtyAt, lastActivityAt, finishedAt, costMs });
63
+ }
64
+
65
+ function schedule() {
66
+ if (!pending || running) return;
67
+ const at = dueAt();
68
+ if (timer !== null) {
69
+ if (timerAt === at) return;
70
+ clearTimer(timer);
71
+ }
72
+ timerAt = at;
73
+ timer = setTimer(fire, Math.max(0, at - now()));
74
+ }
75
+
76
+ function fire() {
77
+ timer = null;
78
+ timerAt = null;
79
+ if (!pending || running) return;
80
+ if (now() < dueAt()) {
81
+ schedule();
82
+ return;
83
+ }
84
+ admit();
85
+ }
86
+
87
+ function admit() {
88
+ pending = false;
89
+ running = true;
90
+ firstDirtyAt = null;
91
+ lastActivityAt = null;
92
+ let attempt;
93
+ try {
94
+ attempt = run();
95
+ } catch (err) {
96
+ finish();
97
+ return;
98
+ }
99
+ Promise.resolve(attempt).then(finish, finish);
100
+ }
101
+
102
+ function finish() {
103
+ running = false;
104
+ finishedAt = now();
105
+ if (pending) schedule();
106
+ }
107
+
108
+ function noteActivity({ at = now() } = {}) {
109
+ if (firstDirtyAt === null) firstDirtyAt = at;
110
+ lastActivityAt = at;
111
+ pending = true;
112
+ schedule();
113
+ }
114
+
115
+ // An attempt asked for by something other than user activity (a merge that kept
116
+ // this tab's edits). It respects holds and the cost spacing, and it invents no
117
+ // input timestamp: with none recorded the idle and continuous terms are already
118
+ // past, so the attempt is due as soon as the gap after the last save allows.
119
+ function request() {
120
+ pending = true;
121
+ schedule();
122
+ }
123
+
124
+ // Cost of the synchronous preparation and completion work of the last admitted
125
+ // attempt. React at once to a slow save, decay gradually. Skipped or coalesced
126
+ // attempts report nothing and must not zero the estimate.
127
+ function noteCost(sampleMs) {
128
+ if (!(sampleMs > 0)) return;
129
+ costMs = Math.max(sampleMs, costMs * 0.8);
130
+ }
131
+
132
+ return {
133
+ noteActivity,
134
+ request,
135
+ noteCost,
136
+ dueAt,
137
+ costEstimate: () => costMs,
138
+ pending: () => pending || running,
139
+ running: () => running
140
+ };
141
+ }
@@ -0,0 +1,8 @@
1
+ export const BOOT_IMPORTS = {
2
+ "plugins/sortable.js": ["vendor/Sortable.vendor.js"],
3
+ "plugins/derive.js": ["vendor/sap-core.vendor.js"],
4
+ };
5
+ export const EDIT_BOOT_IMPORTS = ["core/host-permission.js"];
6
+ // Code a Document's edit boot fetches before it can take input. The editor is a
7
+ // dynamic target of its own, so only a plan that runs the page plugin preloads it.
8
+ export const EDITOR_BOOT_IMPORTS = ["page/editor.js"];
@@ -9,7 +9,9 @@ import { bootPhase } from "../core/boot-phase.js";
9
9
  // from its own attributes, so the same answer holds for the live page, a
10
10
  // snapshot clone and a parsed frame. No expression is evaluated here.
11
11
 
12
- export const isDerivedAttr = (el, name) => ownsAttr(el, name);
12
+ // `host` is the element a just-removed subtree belongs under (the undo engine passes
13
+ // it), so a detached card is read as painted the way it was while attached.
14
+ export const isDerivedAttr = (el, name, host = null) => ownsAttr(el, name, host);
13
15
 
14
16
  export const ownsDerivedText = (el) => !!el && el.nodeType === 1 && !!paintsOf(el)?.text;
15
17