@panphora/clayjs 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (511) hide show
  1. package/dist/all.js +1 -1
  2. package/dist/clay-dom.js +1 -1
  3. package/dist/clay-events.js +1 -1
  4. package/dist/clay-internals.js +1 -1
  5. package/dist/clay-options.js +1 -1
  6. package/dist/clay-router.js +1 -1
  7. package/dist/clay-ui.js +1 -1
  8. package/dist/clay-utils.js +1 -1
  9. package/dist/clay.js +1 -1
  10. package/dist/clay.standalone.js +72519 -59839
  11. package/dist/m/attrs/refetch-on-save-ZUE7PSCI.js +1 -0
  12. package/dist/m/attrs/save-freeze-LWGF6XUN.js +1 -0
  13. package/dist/m/c-2GRUM77Q.js +30 -0
  14. package/dist/m/c-2KIEGNC4.js +1 -0
  15. package/dist/m/{c-5R4PTRG2.js → c-2KL3BD52.js} +1 -1
  16. package/dist/m/c-2YYDMLNZ.js +1 -0
  17. package/dist/m/c-2ZZBC45O.js +1 -0
  18. package/dist/m/c-372N6H3B.js +1 -0
  19. package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
  20. package/dist/m/c-3LDGIBMN.js +1 -0
  21. package/dist/m/c-3O7OBDCM.js +1 -0
  22. package/dist/m/c-4ASQPRQL.js +1 -0
  23. package/dist/m/c-4TXNLX4K.js +6 -0
  24. package/dist/m/c-4YU3BR4D.js +6 -0
  25. package/dist/m/c-5734MF2P.js +1 -0
  26. package/dist/m/c-57XDY6HC.js +1 -0
  27. package/dist/m/{c-VFX45IZQ.js → c-5B4YBZXC.js} +1 -1
  28. package/dist/m/c-5ONTYIUI.js +1 -0
  29. package/dist/m/c-5OOVFCJM.js +1 -0
  30. package/dist/m/c-5T6NZHNM.js +1 -0
  31. package/dist/m/{c-YDH7Q3NY.js → c-5ZQV7KC4.js} +1 -1
  32. package/dist/m/{c-Z36J63PP.js → c-6DB2OLRJ.js} +1 -1
  33. package/dist/m/c-6DNRCPNM.js +1 -0
  34. package/dist/m/c-6UGWFMQC.js +2 -0
  35. package/dist/m/c-7K4SCQB7.js +1 -0
  36. package/dist/m/c-7MG5RU63.js +1 -0
  37. package/dist/m/c-7PRJCA3E.js +1 -0
  38. package/dist/m/c-A6L3FMUS.js +1 -0
  39. package/dist/m/c-AMU3BPPR.js +4 -0
  40. package/dist/m/c-AMYEFA2E.js +1 -0
  41. package/dist/m/c-AQYCG7QW.js +1 -0
  42. package/dist/m/c-ATKAOH23.js +1 -0
  43. package/dist/m/c-B3JM5ZXC.js +1 -0
  44. package/dist/m/c-B5CS55WR.js +1 -0
  45. package/dist/m/c-B5UJQT7W.js +1 -0
  46. package/dist/m/c-B7J4K2AE.js +3 -0
  47. package/dist/m/c-BHZYSHW4.js +1 -0
  48. package/dist/m/c-BIJALSRD.js +1 -0
  49. package/dist/m/c-BJ7YQ5QZ.js +1 -0
  50. package/dist/m/c-BPF2NSPV.js +1 -0
  51. package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
  52. package/dist/m/c-BUAYFEV5.js +1 -0
  53. package/dist/m/c-C6IPRG34.js +1 -0
  54. package/dist/m/{c-2JL6E75N.js → c-CAS3NND4.js} +1 -1
  55. package/dist/m/{c-4DRMS4QG.js → c-CCQ25GFY.js} +1 -1
  56. package/dist/m/c-CJFDLD43.js +1 -0
  57. package/dist/m/c-COPTL5WB.js +1 -0
  58. package/dist/m/c-D3E4WJ4M.js +166 -0
  59. package/dist/m/c-D5IKEKXW.js +7 -0
  60. package/dist/m/c-DHBRA6DL.js +1 -0
  61. package/dist/m/c-DKLS74XB.js +72 -0
  62. package/dist/m/c-DVYGFTSK.js +3 -0
  63. package/dist/m/c-E3ZXARWA.js +1 -0
  64. package/dist/m/c-EK63G3CG.js +1 -0
  65. package/dist/m/c-ERQSNR6K.js +13 -0
  66. package/dist/m/c-EVYAISP3.js +1 -0
  67. package/dist/m/c-EXKHPOVC.js +3 -0
  68. package/dist/m/{c-7VGZ7RU5.js → c-FWKHM4E7.js} +1 -1
  69. package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
  70. package/dist/m/c-GNDL3TKB.js +1 -0
  71. package/dist/m/c-GRWMD6O5.js +1 -0
  72. package/dist/m/c-GRXHHFPO.js +1 -0
  73. package/dist/m/{c-OYTFP5QN.js → c-GY422N3Q.js} +1 -1
  74. package/dist/m/c-HAWFNBYG.js +1 -0
  75. package/dist/m/c-HG2C77BM.js +4 -0
  76. package/dist/m/c-HHGJBU4D.js +1 -0
  77. package/dist/m/c-HMV6P4RX.js +1 -0
  78. package/dist/m/c-HMYWIHFG.js +1 -0
  79. package/dist/m/c-HT2DKBV2.js +1 -0
  80. package/dist/m/c-I2SCCUEW.js +1 -0
  81. package/dist/m/{c-HRDCD3WN.js → c-ICJYHABF.js} +2 -2
  82. package/dist/m/c-IMZCWYHR.js +7 -0
  83. package/dist/m/c-IVMD3FEM.js +1 -0
  84. package/dist/m/c-IVTRVENZ.js +1 -0
  85. package/dist/m/c-IW6GGOU2.js +1 -0
  86. package/dist/m/c-JKZJIUIP.js +1 -0
  87. package/dist/m/{c-NZMNXVF6.js → c-JNLX3RJB.js} +1 -1
  88. package/dist/m/c-JRKY44DK.js +1 -0
  89. package/dist/m/c-K3ADYNLU.js +1 -0
  90. package/dist/m/c-KBS3UV6B.js +1 -0
  91. package/dist/m/c-KGHVFRR3.js +1 -0
  92. package/dist/m/c-KJJPV77R.js +1 -0
  93. package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
  94. package/dist/m/{c-5Y6TNX5W.js → c-KP64LT2A.js} +2 -2
  95. package/dist/m/c-KQ7E3PYY.js +1 -0
  96. package/dist/m/c-L3F5ZLIV.js +1 -0
  97. package/dist/m/{c-LR4CGEPT.js → c-L42OLXWM.js} +3 -2
  98. package/dist/m/c-L5VFATT6.js +1 -0
  99. package/dist/m/c-L7JL5TL3.js +3 -0
  100. package/dist/m/c-LKSL4AUJ.js +1 -0
  101. package/dist/m/c-M6FNQCTQ.js +1 -0
  102. package/dist/m/c-MCOOHCSG.js +1 -0
  103. package/dist/m/c-MHI3GYKF.js +1 -0
  104. package/dist/m/c-MWXFLW4J.js +1 -0
  105. package/dist/m/c-NEL7HQW3.js +1 -0
  106. package/dist/m/c-NQJRVJ56.js +1 -0
  107. package/dist/m/c-NWX3MFT4.js +1 -0
  108. package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
  109. package/dist/m/c-OKXI52N7.js +1 -0
  110. package/dist/m/{c-MMUIUUZG.js → c-OPLUZR3A.js} +1 -1
  111. package/dist/m/{c-J7CA54IP.js → c-OTMKN2OO.js} +1 -1
  112. package/dist/m/{c-LTGMXXKO.js → c-OYRZ7XG2.js} +1 -1
  113. package/dist/m/c-P3OFYUMA.js +1 -0
  114. package/dist/m/c-PLN76GF3.js +6 -0
  115. package/dist/m/c-PLP4JKFC.js +1 -0
  116. package/dist/m/c-Q26MME6N.js +1 -0
  117. package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
  118. package/dist/m/{c-SWRJFCNN.js → c-QIKT6SKM.js} +1 -1
  119. package/dist/m/c-QIXT3Q7C.js +28 -0
  120. package/dist/m/c-QO4HNGCQ.js +1 -0
  121. package/dist/m/c-R27DWQKX.js +1 -0
  122. package/dist/m/{c-HVYXIS72.js → c-R2XUODVY.js} +1 -1
  123. package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
  124. package/dist/m/c-RND3G2S2.js +1 -0
  125. package/dist/m/c-S2U6NJU4.js +1 -0
  126. package/dist/m/c-SPZPGD3R.js +1 -0
  127. package/dist/m/c-SW4YEG2D.js +8 -0
  128. package/dist/m/c-SZGYW5HT.js +1 -0
  129. package/dist/m/c-T637T7SN.js +1 -0
  130. package/dist/m/c-TJAXGIRV.js +1 -0
  131. package/dist/m/{c-KKWLDOPM.js → c-TNEM3RNM.js} +1 -1
  132. package/dist/m/c-TTB5EK4S.js +32 -0
  133. package/dist/m/c-U7SHNWDH.js +1 -0
  134. package/dist/m/c-UDHIVLYH.js +20 -0
  135. package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
  136. package/dist/m/{c-TQZ2JYHD.js → c-UORXLSD3.js} +1 -1
  137. package/dist/m/c-UPD33ZPE.js +1 -0
  138. package/dist/m/c-URSMSFCB.js +1 -0
  139. package/dist/m/c-UY4QX42B.js +1 -0
  140. package/dist/m/c-V44KWI2L.js +4 -0
  141. package/dist/m/c-VG5XIN4T.js +1 -0
  142. package/dist/m/c-VILENZTD.js +1 -0
  143. package/dist/m/{c-33HUYEDI.js → c-VJYEVRYK.js} +1 -1
  144. package/dist/m/c-VR2BJ4AN.js +1 -0
  145. package/dist/m/c-W4JCVLAB.js +1 -0
  146. package/dist/m/c-W6DQSHXI.js +135 -0
  147. package/dist/m/c-WBGWGOM7.js +1 -0
  148. package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
  149. package/dist/m/c-WGW764WX.js +1 -0
  150. package/dist/m/c-WMRJKQ2V.js +5 -0
  151. package/dist/m/c-WNTYKCDG.js +1 -0
  152. package/dist/m/c-WQWIRWO3.js +72 -0
  153. package/dist/m/c-X5LNHET5.js +1 -0
  154. package/dist/m/c-X5TSXZYJ.js +2 -0
  155. package/dist/m/c-XGH4GS2T.js +1 -0
  156. package/dist/m/c-Y6F2FORL.js +1 -0
  157. package/dist/m/c-YBKHUAK7.js +10 -0
  158. package/dist/m/c-YBSS55KU.js +20 -0
  159. package/dist/m/c-YCLU4UNT.js +2 -0
  160. package/dist/m/c-YVZ2L7CE.js +1 -0
  161. package/dist/m/c-ZK4GIZH2.js +1 -0
  162. package/dist/m/c-ZRXJD4G6.js +1 -0
  163. package/dist/m/c-ZWBI4HYK.js +1 -0
  164. package/dist/m/{c-HT33EQRN.js → c-ZWIOJGMT.js} +1 -1
  165. package/dist/m/core/admin-attrs-YY7T5FGU.js +1 -0
  166. package/dist/m/core/autosave-S6OMNDCK.js +1 -0
  167. package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
  168. package/dist/m/core/host-permission-6EVJGNPL.js +1 -0
  169. package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
  170. package/dist/m/core/{persist-HKHA66IY.js → persist-UKXV5KLA.js} +1 -1
  171. package/dist/m/core/recovery-DRFZRJED.js +1 -0
  172. package/dist/m/core/save-LYY7K6U7.js +1 -0
  173. package/dist/m/core/save-core-MKRPSEEO.js +1 -0
  174. package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
  175. package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
  176. package/dist/m/events/index-RN2JNGBV.js +1 -0
  177. package/dist/m/internals/index-TGLQINN3.js +1 -0
  178. package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
  179. package/dist/m/loader-3BBNZZGM.js +1 -0
  180. package/dist/m/manifest-F449FF98.js +1 -0
  181. package/dist/m/manifest-edit-4396A009.js +1 -0
  182. package/dist/m/manifest.json +2509 -1474
  183. package/dist/m/options/options-EJMS3K24.js +1 -0
  184. package/dist/m/page/editor-6LRB3GE4.js +304 -0
  185. package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
  186. package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
  187. package/dist/m/plugins/demo-OYO5GCAO.js +1 -0
  188. package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
  189. package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
  190. package/dist/m/plugins/live-AD4H6XSL.js +1 -0
  191. package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
  192. package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
  193. package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
  194. package/dist/m/plugins/people-JCHVPF7P.js +1 -0
  195. package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
  196. package/dist/m/plugins/{richclay-RC3L2F6X.js → richclay-KLGETK77.js} +2 -2
  197. package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
  198. package/dist/m/plugins/source-TJXOIOYY.js +1 -0
  199. package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
  200. package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
  201. package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
  202. package/dist/m/plugins/upload-NHGJXZK2.js +1 -0
  203. package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
  204. package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
  205. package/dist/m/plugins/wire-VBWXEXU2.js +1 -0
  206. package/dist/m/reader-core-4W2KYEWQ.js +77 -0
  207. package/dist/m/route/route-62CHXL5B.js +1 -0
  208. package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
  209. package/dist/m/ui/index-IVWN2QC4.js +1 -0
  210. package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
  211. package/dist/offline-manifest.json +1560 -889
  212. package/dist/page/themes/light-1.css +33 -8
  213. package/dist/server/core.cjs +1554 -311
  214. package/dist/server/offline-runtime.cjs +63 -63
  215. package/dist/server/offline-runtime.mjs +63 -63
  216. package/dist/server/page-compaction-worker.cjs +3710 -0
  217. package/dist/server/rooms.mjs +5 -0
  218. package/docs/guide.md +653 -101
  219. package/entries/sap.js +5 -5
  220. package/package.json +1 -1
  221. package/src/core/admin-contenteditable.js +5 -2
  222. package/src/core/admin-inputs.js +1 -0
  223. package/src/core/admin-onclick.js +2 -1
  224. package/src/core/boot-phase.js +2 -1
  225. package/src/core/conflict-download.js +3 -29
  226. package/src/core/conflict-notice-ui.js +4 -7
  227. package/src/core/download.js +32 -0
  228. package/src/core/host-meta.js +56 -0
  229. package/src/core/is-edit-mode.js +17 -1
  230. package/src/core/mode-key.js +125 -19
  231. package/src/core/mode-support.js +5 -0
  232. package/src/core/mode-transition.js +20 -6
  233. package/src/core/preserve-authored.js +4 -4
  234. package/src/core/recovery.js +121 -33
  235. package/src/core/save-accepted.js +33 -0
  236. package/src/core/save-core.js +111 -32
  237. package/src/core/save-job.js +108 -0
  238. package/src/core/save-prepare.js +53 -0
  239. package/src/core/save-size-notice.js +3 -1
  240. package/src/core/save-slices.js +15 -0
  241. package/src/core/save.js +557 -127
  242. package/src/core/serialize-steps.js +109 -0
  243. package/src/core/snapshot.js +209 -40
  244. package/src/core/source-map.js +83 -38
  245. package/src/core/stale-host-notice.js +1 -0
  246. package/src/core/upgrade-run.js +59 -8
  247. package/src/dom/active-element.js +5 -0
  248. package/src/lib/assets.js +1 -0
  249. package/src/lib/autosave-scheduler.js +3 -4
  250. package/src/lib/slice.js +65 -0
  251. package/src/lib/uimotion.js +774 -0
  252. package/src/lib/unsaved-state.js +2 -2
  253. package/src/live/fast-lane.js +10 -3
  254. package/src/live/local-lane.js +202 -11
  255. package/src/live/ops.js +16 -0
  256. package/src/live/page-compaction.js +162 -0
  257. package/src/live/server-lane.js +1 -0
  258. package/src/live/store.js +11 -3
  259. package/src/loader-logic.js +3 -1
  260. package/src/loader.js +14 -4
  261. package/src/page/adopt-boot.js +2 -1
  262. package/src/page/awareness.js +33 -2
  263. package/src/page/blocks.js +122 -27
  264. package/src/page/board-views.js +2 -16
  265. package/src/page/boot-pack.js +1 -1
  266. package/src/page/chart-tables.js +29 -0
  267. package/src/page/chart.js +411 -72
  268. package/src/page/checklist.js +10 -135
  269. package/src/page/checkpoint-proof.js +295 -0
  270. package/src/page/chrome.js +257 -40
  271. package/src/page/clipboard-shape.js +126 -0
  272. package/src/page/document-title-dom.js +12 -0
  273. package/src/page/document-title-runtime.js +15 -1
  274. package/src/page/document-title.js +4 -1
  275. package/src/page/drag.js +33 -3
  276. package/src/page/editor.js +207 -31
  277. package/src/page/emoji-data.js +162 -0
  278. package/src/page/export-assets.js +357 -0
  279. package/src/page/export-html.js +209 -0
  280. package/src/page/export-markdown.js +361 -0
  281. package/src/page/features/board.js +12 -3
  282. package/src/page/features/chart.js +11 -0
  283. package/src/page/features/checklist.js +428 -0
  284. package/src/page/features/dates.js +180 -0
  285. package/src/page/features/emoji.js +187 -0
  286. package/src/page/features/export-html.js +189 -0
  287. package/src/page/features/export-markdown.js +169 -0
  288. package/src/page/features/import-markdown.js +349 -0
  289. package/src/page/features/media.js +61 -5
  290. package/src/page/features/mentions.js +188 -0
  291. package/src/page/features/registry.js +24 -1
  292. package/src/page/features/table.js +375 -85
  293. package/src/page/features/title-cover.js +331 -0
  294. package/src/page/features/title-icon.js +213 -0
  295. package/src/page/features/toc.js +87 -0
  296. package/src/page/format-command.js +33 -0
  297. package/src/page/format-gate.js +68 -18
  298. package/src/page/format-host.js +71 -0
  299. package/src/page/format-native-commands.js +158 -0
  300. package/src/page/format-native.js +42 -0
  301. package/src/page/image-actions.js +64 -0
  302. package/src/page/import-markdown.js +262 -0
  303. package/src/page/inline.js +32 -8
  304. package/src/page/items.js +141 -11
  305. package/src/page/markdown.js +30 -2
  306. package/src/page/media.js +35 -119
  307. package/src/page/mention-picker.js +99 -0
  308. package/src/page/merge.js +1 -1
  309. package/src/page/placeholder.js +14 -1
  310. package/src/page/projection.js +156 -25
  311. package/src/page/recover.js +10 -12
  312. package/src/page/recovery-ui.js +464 -0
  313. package/src/page/rich/chart.js +63 -7
  314. package/src/page/save-projection.js +67 -0
  315. package/src/page/save-states.js +14 -4
  316. package/src/page/schema.js +52 -3
  317. package/src/page/table-paste.js +3 -1
  318. package/src/page/table-style.js +4 -4
  319. package/src/page/table.js +44 -2
  320. package/src/page/themes/content.css +25 -1
  321. package/src/page/themes/light-1.css +33 -8
  322. package/src/page/themes/light-1.source.css +8 -7
  323. package/src/page/toc-link.js +31 -0
  324. package/src/page/toc.js +5 -0
  325. package/src/page/view-hooks.js +58 -0
  326. package/src/page/view.js +13 -0
  327. package/src/page/ybind/binding.js +116 -6
  328. package/src/page/ybind/bridge.js +187 -20
  329. package/src/page/ybind/containment.js +162 -0
  330. package/src/page/ybind/doc.js +17 -9
  331. package/src/page/ybind/file-debt.js +63 -0
  332. package/src/page/ybind/layout.js +7 -2
  333. package/src/page/ybind/marks.js +2 -1
  334. package/src/page/ybind/moves.js +5 -4
  335. package/src/page/ybind/origins.js +106 -4
  336. package/src/page/ybind/parts.js +8 -1
  337. package/src/page/ybind/provider.js +125 -18
  338. package/src/page/ybind/recovery-proof.js +32 -9
  339. package/src/page/ybind/recovery-replay.js +905 -4
  340. package/src/page/ybind/recovery.js +174 -45
  341. package/src/page/ybind/table.js +67 -11
  342. package/src/page/ybind/walk-delta.js +258 -0
  343. package/src/page/ybind/walk.js +54 -29
  344. package/src/page/ybind/writer.js +1 -1
  345. package/src/plugins/ai-edit.js +13 -9
  346. package/src/plugins/indicator-chip.js +91 -0
  347. package/src/plugins/indicator.js +13 -80
  348. package/src/plugins/live.js +126 -15
  349. package/src/plugins/menu/actions.js +196 -0
  350. package/src/plugins/menu/people.js +279 -0
  351. package/src/plugins/menu/placement.js +121 -0
  352. package/src/plugins/menu/preferences.js +9 -0
  353. package/src/plugins/menu/seo.js +316 -397
  354. package/src/plugins/menu/style.js +171 -0
  355. package/src/plugins/menu/variants.js +296 -0
  356. package/src/plugins/menu.js +186 -183
  357. package/src/plugins/page-view.js +3 -2
  358. package/src/plugins/page.js +362 -43
  359. package/src/plugins/sortable.js +8 -1
  360. package/src/plugins/source.js +6 -0
  361. package/src/plugins/upgrade.js +51 -17
  362. package/src/plugins/upload-ui.js +12 -17
  363. package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
  364. package/src/plugins/visitor-drafts/intro-style.js +7 -2
  365. package/src/plugins/visitor-drafts/intro.js +7 -4
  366. package/src/plugins/visitor-drafts/owner-chip.js +8 -12
  367. package/src/plugins/visitor-drafts/prompt.js +11 -9
  368. package/src/plugins/visitor-drafts/source.js +4 -3
  369. package/src/plugins/visitor-drafts/store.js +138 -9
  370. package/src/plugins/visitor-drafts/ui-memory.js +2 -2
  371. package/src/plugins/visitor-drafts-owner.js +4 -6
  372. package/src/plugins/visitor-drafts.js +5 -3
  373. package/src/recovery/store.js +1 -1
  374. package/src/route/route.js +4 -0
  375. package/src/server/checkpoint/index.js +2 -2
  376. package/src/server/rooms/index.js +5 -1
  377. package/src/server/rooms/live-data-rooms.js +302 -50
  378. package/src/server/rooms/page-compaction-worker.js +12 -0
  379. package/src/server/rooms/page-compactor.js +92 -0
  380. package/src/server/rooms/page-room-conformance.js +461 -0
  381. package/src/source/parse-steps.js +94 -0
  382. package/src/source/session.js +233 -57
  383. package/src/sync/editing-sender.js +12 -0
  384. package/src/sync/live-sync-edit.js +22 -2
  385. package/src/sync/live-sync.js +150 -23
  386. package/src/sync/page-relay.js +52 -0
  387. package/src/sync/presence-store.js +44 -4
  388. package/src/sync/presence.js +6 -3
  389. package/src/sync/saved-stream.js +28 -14
  390. package/src/ui/bevel-controls.js +1 -0
  391. package/src/ui/bevel-dialog.js +5 -2
  392. package/src/ui/editor-ui.js +77 -0
  393. package/src/ui/page-colour.js +281 -0
  394. package/src/ui/toast.js +16 -7
  395. package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
  396. package/src/vendor/hyper-undo.vendor.js +1 -1
  397. package/src/vendor/hypercms.vendor.js +9 -9
  398. package/src/vendor/parse5.vendor.js +2 -2
  399. package/src/vendor/sap-core.vendor.js +5 -5
  400. package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +0 -1
  401. package/dist/m/attrs/save-freeze-FUP4Q4X3.js +0 -1
  402. package/dist/m/c-2M73252O.js +0 -1
  403. package/dist/m/c-2SPVQSHC.js +0 -1
  404. package/dist/m/c-35NBGMZB.js +0 -1
  405. package/dist/m/c-3IQRECYS.js +0 -1
  406. package/dist/m/c-4B6H4G5V.js +0 -1
  407. package/dist/m/c-4FFHZM32.js +0 -1
  408. package/dist/m/c-4YSCRICO.js +0 -1
  409. package/dist/m/c-54RCDCBU.js +0 -1
  410. package/dist/m/c-5M4YEKCX.js +0 -4
  411. package/dist/m/c-5V3MOVJT.js +0 -1
  412. package/dist/m/c-66O32MV3.js +0 -28
  413. package/dist/m/c-6HMB24EV.js +0 -1
  414. package/dist/m/c-7O6AFR65.js +0 -1
  415. package/dist/m/c-ANQTJ3B5.js +0 -2
  416. package/dist/m/c-AYVOREBE.js +0 -1
  417. package/dist/m/c-B2ZXPOS3.js +0 -3
  418. package/dist/m/c-BMIZKMPV.js +0 -1
  419. package/dist/m/c-BTDI4Y23.js +0 -1
  420. package/dist/m/c-CJZSLMVQ.js +0 -1
  421. package/dist/m/c-DBE6ORSI.js +0 -1
  422. package/dist/m/c-EDHUR4MX.js +0 -1
  423. package/dist/m/c-EY6UOM54.js +0 -1
  424. package/dist/m/c-F3CKJTJS.js +0 -1
  425. package/dist/m/c-F7Y6FLPY.js +0 -1
  426. package/dist/m/c-FA4DZVTC.js +0 -1
  427. package/dist/m/c-G2BUZITR.js +0 -1
  428. package/dist/m/c-GB3JTV6E.js +0 -1
  429. package/dist/m/c-GBGWXTX4.js +0 -1
  430. package/dist/m/c-GHBCCVG4.js +0 -1
  431. package/dist/m/c-GJAEGGXY.js +0 -1
  432. package/dist/m/c-GOWBDM6J.js +0 -1
  433. package/dist/m/c-IB52R237.js +0 -1
  434. package/dist/m/c-IKWNE4R4.js +0 -1
  435. package/dist/m/c-IU3IYEU6.js +0 -1
  436. package/dist/m/c-IZ2EBRAJ.js +0 -1
  437. package/dist/m/c-KR4UW4KW.js +0 -1
  438. package/dist/m/c-KY3VJGUA.js +0 -8
  439. package/dist/m/c-LFPHCXY3.js +0 -1
  440. package/dist/m/c-LRPUO2MX.js +0 -1
  441. package/dist/m/c-LXENEBV7.js +0 -1
  442. package/dist/m/c-NMJRXKPC.js +0 -8
  443. package/dist/m/c-OIBUTDXI.js +0 -1
  444. package/dist/m/c-PPOSCAQD.js +0 -1
  445. package/dist/m/c-QIX5BJYN.js +0 -1
  446. package/dist/m/c-QQHEX6HA.js +0 -1
  447. package/dist/m/c-R7RMANJI.js +0 -9
  448. package/dist/m/c-RI4NT75Y.js +0 -1
  449. package/dist/m/c-RM7J7BV2.js +0 -1
  450. package/dist/m/c-RUJKSUQW.js +0 -1
  451. package/dist/m/c-SGCCC4FW.js +0 -25
  452. package/dist/m/c-T4VO4OCB.js +0 -1
  453. package/dist/m/c-TQEMVJCS.js +0 -1
  454. package/dist/m/c-TR6RMDQ7.js +0 -3
  455. package/dist/m/c-UKHTLWUW.js +0 -1
  456. package/dist/m/c-UNN7LWCT.js +0 -1
  457. package/dist/m/c-UXBMRDZB.js +0 -235
  458. package/dist/m/c-V7PZZRG7.js +0 -1
  459. package/dist/m/c-VL52ADDR.js +0 -1
  460. package/dist/m/c-WNYBLIOZ.js +0 -1
  461. package/dist/m/c-WWDBYXLK.js +0 -1
  462. package/dist/m/c-XNNW2CWK.js +0 -1
  463. package/dist/m/c-Y6USGTS5.js +0 -5
  464. package/dist/m/c-YG2NJYS6.js +0 -135
  465. package/dist/m/c-Z4VRC7GU.js +0 -7
  466. package/dist/m/c-Z6K52OLP.js +0 -19
  467. package/dist/m/c-ZDY2MDAA.js +0 -21
  468. package/dist/m/c-ZUCOKOHT.js +0 -1
  469. package/dist/m/core/admin-attrs-VGNLAUDK.js +0 -1
  470. package/dist/m/core/autosave-E4PEFPZ6.js +0 -1
  471. package/dist/m/core/conflict-notice-VQ6Y64PH.js +0 -1
  472. package/dist/m/core/host-permission-XN3K2IW3.js +0 -1
  473. package/dist/m/core/is-edit-mode-JXHDVGUL.js +0 -1
  474. package/dist/m/core/recovery-TBBSICE2.js +0 -1
  475. package/dist/m/core/save-WUCHTQBE.js +0 -1
  476. package/dist/m/core/save-core-FJVHT5M3.js +0 -1
  477. package/dist/m/core/snapshot-F3SWDZMM.js +0 -1
  478. package/dist/m/core/unsaved-warning-STKBHE6G.js +0 -1
  479. package/dist/m/events/index-23DPTUT4.js +0 -1
  480. package/dist/m/internals/index-WOVAP3RU.js +0 -1
  481. package/dist/m/lib/cache-bust-CGWT6ZJI.js +0 -1
  482. package/dist/m/loader-HXX6YNH5.js +0 -1
  483. package/dist/m/manifest-F22DEDAC.js +0 -1
  484. package/dist/m/manifest-edit-C3C7376B.js +0 -1
  485. package/dist/m/options/options-2LH76JQ3.js +0 -1
  486. package/dist/m/page/editor-XUTB2HB2.js +0 -189
  487. package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +0 -6
  488. package/dist/m/plugins/demo-ZIBFAA3R.js +0 -1
  489. package/dist/m/plugins/indicator-PV4YW2ID.js +0 -1
  490. package/dist/m/plugins/live-NUTTXSBS.js +0 -1
  491. package/dist/m/plugins/menu-HCKO3DSP.js +0 -138
  492. package/dist/m/plugins/page-KSUH7G6J.js +0 -2
  493. package/dist/m/plugins/page-view-W73OW7WI.js +0 -1
  494. package/dist/m/plugins/people-AJJ32YCG.js +0 -1
  495. package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
  496. package/dist/m/plugins/sortable-YRMQX3ZU.js +0 -1
  497. package/dist/m/plugins/source-KHK5UUSK.js +0 -1
  498. package/dist/m/plugins/undo-OF2L3CXU.js +0 -1
  499. package/dist/m/plugins/upgrade-ZNYODP5Y.js +0 -1
  500. package/dist/m/plugins/upload-XSGNILUY.js +0 -1
  501. package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +0 -21
  502. package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +0 -1
  503. package/dist/m/plugins/wire-ZWVWKF76.js +0 -1
  504. package/dist/m/reader-core-ILWLXVMP.js +0 -68
  505. package/dist/m/route/route-KOCZSEGN.js +0 -1
  506. package/dist/m/sync/live-sync-4AUVXVJU.js +0 -1
  507. package/dist/m/ui/index-EVHKMKBL.js +0 -1
  508. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
  509. /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
  510. /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
  511. /package/dist/m/plugins/{richclay-RC3L2F6X.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
@@ -0,0 +1,77 @@
1
+ import { addRegionToken } from "../lib/region-policy.js";
2
+
3
+ // Every interface ClayJS draws over a document mounts through here, inside an open
4
+ // shadow root, so the document's own CSS cannot restyle it. The host takes no box
5
+ // (display: contents) and stops inheritance at the boundary (all: initial); custom
6
+ // properties still cross, which is how theme tokens arrive.
7
+ export const HOST_CSS = `:host { all: initial !important; display: contents !important; }
8
+ :host([hidden]) { display: none !important; }
9
+ @media print { :host { display: none !important; } }`;
10
+
11
+ const sheetsByDocument = new WeakMap();
12
+ const guardedWindows = new WeakSet();
13
+
14
+ // A key event from a field inside a root reaches the page's listeners retargeted to the
15
+ // host, so a page's "is someone typing?" check sees no field and runs its shortcut (a
16
+ // "/" that focuses search, an arrow that turns a slide). Typing and caret keys stop at
17
+ // the window, before any page listener; the browser still types and moves the caret.
18
+ // Enter, Escape and Tab pass, since the fields' own handlers use them.
19
+ const CARET_KEYS = new Set(["Backspace", "Delete", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "PageUp", "PageDown"]);
20
+
21
+ function keepTypingInside(event) {
22
+ if (event.ctrlKey || event.metaKey || event.altKey) return;
23
+ if (event.key?.length !== 1 && !CARET_KEYS.has(event.key)) return;
24
+ const target = event.composedPath()[0];
25
+ if (!target?.matches?.("input, textarea, select") && !target?.isContentEditable) return;
26
+ if (target.getRootNode().host?.localName === "clay-editor-ui") event.stopPropagation();
27
+ }
28
+
29
+ function guardTyping(win) {
30
+ if (!win || guardedWindows.has(win)) return;
31
+ guardedWindows.add(win);
32
+ for (const type of ["keydown", "keypress", "keyup"]) win.addEventListener(type, keepTypingInside, true);
33
+ }
34
+
35
+ function adopts(doc, shadow) {
36
+ return "adoptedStyleSheets" in shadow && typeof doc.defaultView?.CSSStyleSheet?.prototype?.replaceSync === "function";
37
+ }
38
+
39
+ function sheetFor(doc, text) {
40
+ let sheets = sheetsByDocument.get(doc);
41
+ if (!sheets) sheetsByDocument.set(doc, (sheets = new Map()));
42
+ let sheet = sheets.get(text);
43
+ if (!sheet) {
44
+ sheet = new doc.defaultView.CSSStyleSheet();
45
+ sheet.replaceSync(text);
46
+ sheets.set(text, sheet);
47
+ }
48
+ return sheet;
49
+ }
50
+
51
+ // The region policy walks parentElement, which stops at a shadow root, so the inner
52
+ // content needs its own editor-ui marker; the host's keeps it out of every capture.
53
+ export function mountEditorUI({ name, content, css = [], parent = document.body, before = null, attributes = {} }) {
54
+ const doc = parent.ownerDocument;
55
+ const host = doc.createElement("clay-editor-ui");
56
+ host.setAttribute("data-clay-ui", name);
57
+ addRegionToken(host, "editor-ui");
58
+ for (const [key, value] of Object.entries(attributes)) host.setAttribute(key, value);
59
+ addRegionToken(content, "editor-ui");
60
+
61
+ const shadow = host.attachShadow({ mode: "open" });
62
+ const texts = [HOST_CSS, ...css];
63
+ if (adopts(doc, shadow)) {
64
+ shadow.adoptedStyleSheets = texts.map((text) => sheetFor(doc, text));
65
+ } else {
66
+ for (const text of texts) {
67
+ const style = doc.createElement("style");
68
+ style.textContent = text;
69
+ shadow.append(style);
70
+ }
71
+ }
72
+ shadow.append(content);
73
+ guardTyping(doc.defaultView);
74
+ parent.insertBefore(host, before);
75
+
76
+ return { host, shadow, content, remove: () => host.remove() };
77
+ }
@@ -0,0 +1,281 @@
1
+ // The colour painted behind a viewport rectangle, read from the DOM, so ClayJS UI can
2
+ // take the page's scheme and lean its surface toward the page instead of guessing from
3
+ // the reader's preference. It answers null when it cannot tell (photos, video, canvas,
4
+ // gradients, busy patterns, split regions), and the UI then keeps Bevel's own look.
5
+ // Measured on about 1,000 real pages: right on 86% of flat regions, about 1 answer in
6
+ // 10,000 regions with the wrong scheme (plans/starter-templates/page-colour/report.md).
7
+ //
8
+ // Per point: the elementsFromPoint stack (open shadow roots spliced in, ClayJS UI
9
+ // skipped), plus pointer-events:none painters that hit-testing cannot see (a census
10
+ // refreshed every 15 s), each layer's background composited front to back over the
11
+ // canvas colour, with ancestor opacity. Unknown layers are folded as black and as
12
+ // white; if the two disagree, the point is unknown. Fifteen points vote in OKLab.
13
+ //
14
+ // It imports nothing: under jest's ESM linker, a module with imports of its own that
15
+ // joins bevel-controls.js's graph fails dynamic imports of bevel-dialog.js with "not in
16
+ // cache". So page-scheme.js's rule is repeated here and the inline !important is direct.
17
+
18
+ const pageScheme = () => {
19
+ const s = getComputedStyle(document.documentElement).colorScheme;
20
+ return s === 'light' || s === 'dark' ? s : 'light dark';
21
+ };
22
+
23
+ // ClayJS's own UI and anything else drawn over the page that is never saved.
24
+ const UI = '[clay~="editor-ui"], [clay~="no-snapshot"], [editor-ui], [no-snapshot], [snapshot-remove]';
25
+ const REPLACED = /^(img|video|canvas|iframe|embed|object|image|use|path|rect|circle|ellipse|line|poly(line|gon))$/;
26
+ const BAD = /(invert|brightness|contrast|grayscale|sepia|hue-rotate|opacity|url)\(/;
27
+ const TO_LMS = [0.4122, 0.5363, 0.0514, 0.2119, 0.6807, 0.1074, 0.0883, 0.2817, 0.63];
28
+ const TO_LAB = [0.2105, 0.7936, -0.0041, 1.978, -2.4286, 0.4506, 0.0259, 0.7828, -0.8087];
29
+ const FROM_LAB = [1, 0.3963, 0.2158, 1, -0.1056, -0.0639, 1, -0.0895, -1.2915];
30
+ const FROM_LMS = [4.0767, -3.3077, 0.231, -1.2684, 2.6098, -0.3413, -0.0042, -0.7034, 1.7076];
31
+
32
+ const clamp = (v) => (v < 0 ? 0 : v > 1 ? 1 : v || 0);
33
+ const multiply = (m, v) => [0, 3, 6].map((k) => m[k] * v[0] + m[k + 1] * v[1] + m[k + 2] * v[2]);
34
+ const lin = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
35
+ const gamma = (c) => clamp(c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);
36
+ const oklab = (rgb) => multiply(TO_LAB, multiply(TO_LMS, rgb.map(lin)).map(Math.cbrt));
37
+ const fromOklab = (lab) => multiply(FROM_LMS, multiply(FROM_LAB, lab).map((v) => v ** 3)).map(gamma);
38
+ const luminance = (rgb) => rgb.map(lin).reduce((sum, c, k) => sum + c * [0.2126, 0.7152, 0.0722][k], 0.05);
39
+ const contrast = (p, q) => Math.max(luminance(p) / luminance(q), luminance(q) / luminance(p));
40
+ const darkInk = (rgb) => luminance(rgb) >= 0.2343;
41
+ const distance = (p, q) => Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
42
+ const average = (list) => [0, 1, 2].map((k) => list.reduce((sum, v) => sum + v[k], 0) / list.length);
43
+ const fade = (c, k) => c && [c[0], c[1], c[2], c[3] * k];
44
+ const inside = (r, x, y) => x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
45
+
46
+ const colours = new Map();
47
+ function colour(text) {
48
+ if (!colours.has(text)) {
49
+ const [, fn, body = ''] = /^(rgba?|oklch|oklab|color)\(([^()]*)\)$/.exec(text) || [];
50
+ const v = body.replace(/^srgb /, '').split(/[\s,/]+/).map((t) => parseFloat(t) || 0);
51
+ const h = (v[2] * Math.PI) / 180;
52
+ const rgb = !fn || (fn === 'color' && !body.startsWith('srgb ')) ? null
53
+ : fn === 'color' ? v.slice(0, 3).map(clamp)
54
+ : fn[0] === 'r' ? v.slice(0, 3).map((c) => clamp(c / 255))
55
+ : fromOklab(fn === 'oklab' ? v : [v[0], v[1] * Math.cos(h), v[1] * Math.sin(h)]);
56
+ colours.set(text, rgb && [...rgb, v.length > 3 ? clamp(v[3]) : 1]);
57
+ }
58
+ return colours.get(text);
59
+ }
60
+
61
+ // A gradient whose stops are all one colour is that colour; any other image is unknown.
62
+ function paint(cs) {
63
+ const image = cs.backgroundImage, c = colour(cs.backgroundColor);
64
+ const stops = (/url\(/.test(image) ? [null] : image.match(/[a-z]+\([^()]*\)/g) || [null]).map(colour);
65
+ const flat = stops.every((s) => s && Math.abs(s[3] - stops[0][3]) < 0.05 && distance(oklab(s), oklab(stops[0])) < 0.02) && stops[0];
66
+ const covers = cs.backgroundRepeat === 'repeat' || /^(auto|cover|100%)( (auto|100%))?$/.test(cs.backgroundSize);
67
+ if (cs.backgroundClip.includes('text')) return [];
68
+ return [
69
+ ...(image !== 'none' && (!flat || flat[3] > 0) ? [{ c: flat, maybe: flat && !covers }] : []),
70
+ ...(!c || c[3] > 0 ? [{ c }] : []),
71
+ ];
72
+ }
73
+
74
+ // Read again every 15 s, or after a second when elements were added or removed.
75
+ let painters = [], censusAt = -Infinity, censusSize = 0;
76
+ function findPainters() {
77
+ const all = document.getElementsByTagName('*'), age = performance.now() - censusAt;
78
+ if (age > 15000 || (age > 1000 && all.length !== censusSize)) {
79
+ censusAt = performance.now();
80
+ censusSize = all.length;
81
+ painters = [...(all.length > 8000 ? [] : all)].filter((el) => {
82
+ const cs = getComputedStyle(el), r = cs.pointerEvents === 'none' && el.getBoundingClientRect();
83
+ return r?.width >= 40 && r.height >= 20 && cs.visibility === 'visible' && !el.closest(UI) && (REPLACED.test(el.localName) || paint(cs).some((l) => !l.c || l.c[3] > 0.03));
84
+ });
85
+ }
86
+ return painters;
87
+ }
88
+
89
+ function deepStack(x, y) {
90
+ const stack = document.elementsFromPoint(x, y), seen = new Set();
91
+ for (let k = 0; k < stack.length; k++) {
92
+ const host = stack[k], root = host.shadowRoot;
93
+ if (root && !seen.has(root) && seen.add(root)) stack.splice(k--, inside(host.getBoundingClientRect(), x, y) ? 0 : 1, ...root.elementsFromPoint(x, y).filter((e) => e.getRootNode() === root));
94
+ }
95
+ return stack;
96
+ }
97
+
98
+ const grid = ({ x, y, width, height }) => [0, 1, 2].flatMap((j) => [0, 1, 2, 3, 4].map((i) => [x + ((i + 0.5 + (j - 1) / 4) * width) / 5, y + ((j + 0.5) * height) / 3]));
99
+
100
+ /**
101
+ * The colour behind a viewport rectangle: { colour: [r, g, b], confidence: 0..1,
102
+ * scheme: 'light' | 'dark' }, or null when it cannot tell.
103
+ */
104
+ export function pageColour(rect) {
105
+ const html = document.documentElement, body = document.body;
106
+ if (typeof document.elementsFromPoint !== 'function' || !body || !(rect.width > 0 && rect.height > 0)) return null;
107
+ const memo = new Map();
108
+ const info = (el) => memo.get(el) || memo.set(el, { el, cs: getComputedStyle(el) }).get(el);
109
+ const rectOf = (i) => (i.rect ??= i.el.getBoundingClientRect());
110
+
111
+ const hcs = getComputedStyle(html);
112
+ const propagated = !paint(hcs).length && body.localName === 'body';
113
+ const owners = propagated ? [html, body] : [html];
114
+ const scheme = hcs.colorScheme === 'normal' ? document.querySelector('meta[name="color-scheme"]')?.content : hcs.colorScheme;
115
+ const dark = /dark/.test(scheme) && (!/light/.test(scheme) || matchMedia('(prefers-color-scheme: dark)').matches);
116
+
117
+ function group(el) {
118
+ const i = info(el), cs = i.cs;
119
+ if (i.k === undefined) {
120
+ const host = el.assignedSlot || el.parentElement || el.parentNode?.host;
121
+ const parent = host ? group(host) : { k: 1 };
122
+ i.k = parent.k * +cs.opacity;
123
+ i.ui = parent.ui || el.matches(UI);
124
+ i.bad = parent.bad || BAD.test(cs.filter + cs.backdropFilter) || cs.mixBlendMode !== 'normal' || /\(/.test(cs.maskImage);
125
+ }
126
+ return i;
127
+ }
128
+
129
+ const ownOf = (i) => (i.own ??= [...(REPLACED.test(i.el.localName) ? [{ c: null }] : []), ...(owners.includes(i.el) ? [] : paint(i.cs))]);
130
+
131
+ // Visible text at a point whose answer it cannot be read on means a painter was missed.
132
+ function inkOf(i) {
133
+ const ink = colour(i.cs.webkitTextFillColor || i.cs.color);
134
+ return (i.ink ??= ink?.[3] >= 0.5 && [...i.el.childNodes].some((n) => n.nodeType === 3 && /\S/.test(n.data)) && ink);
135
+ }
136
+
137
+ function fold(layers, unknown, withMaybe) {
138
+ let rgb = [0, 0, 0], through = 1;
139
+ for (const { c, k = 1, maybe } of layers) {
140
+ const [r, g, b, a] = c || [unknown, unknown, unknown, k];
141
+ const opacity = maybe && !withMaybe ? 0 : a;
142
+ rgb = rgb.map((v, k) => v + through * opacity * [r, g, b][k]);
143
+ through *= 1 - opacity;
144
+ }
145
+ return oklab(rgb);
146
+ }
147
+
148
+ function samplePoint(x, y, found) {
149
+ const raw = deepStack(x, y);
150
+ if (!raw.length) return;
151
+ const unplaced = new Set();
152
+ for (const el of found) {
153
+ const r = rectOf(info(el));
154
+ if (raw.includes(el) || !inside(r, x, y) || !el.isConnected || info(el).cs.visibility !== 'visible') continue;
155
+ let at = raw.findLastIndex((e) => el.contains(e)) + 1;
156
+ if (!at && info(el).cs.zIndex < 0) at = Math.max(0, raw.findIndex((e) => owners.includes(e)));
157
+ if (!at) unplaced.add(el);
158
+ raw.splice(at, 0, el);
159
+ }
160
+ const layers = [];
161
+ let ink = null, covered = false;
162
+ for (const [k, el] of raw.entries()) {
163
+ const i = group(el);
164
+ if (i.ui || !i.k) continue;
165
+ const own = raw.lastIndexOf(el) === k, stray = !owners.includes(el) && !inside(rectOf(i), x, y);
166
+ for (const l of stray ? [{}] : own ? ownOf(i) : []) layers.push({ c: i.bad ? null : fade(l.c, i.k), k: i.k, maybe: l.maybe || unplaced.has(el) });
167
+ if (own && !ink && !covered && !stray) ink = inkOf(i);
168
+ covered ||= stray || (own && ownOf(i).some((l) => !l.maybe && (!l.c || l.c[3] >= 0.5)));
169
+ }
170
+ const root = group(html);
171
+ for (const l of paint(propagated ? getComputedStyle(body) : hcs)) layers.push({ c: root.bad ? null : fade(l.c, root.k) });
172
+ layers.push({ c: dark ? [0.0706, 0.0706, 0.0706, 1] : [1, 1, 1, 1] });
173
+ const variants = [0, 1].flatMap((unknown) => [fold(layers, unknown, false), fold(layers, unknown, true)]);
174
+ if (variants.some((v) => distance(v, variants[0]) > 0.06)) return null;
175
+ const lab = average(variants), rgb = fromOklab(lab);
176
+ const contra = !!ink && contrast([0, 1, 2].map((k) => ink[k] * ink[3] + rgb[k] * (1 - ink[3])), rgb) < 1.25;
177
+ return { lab, contra, approx: layers.some((l) => !l.c || l.maybe) };
178
+ }
179
+
180
+ const found = findPainters();
181
+ const results = grid(rect).map(([x, y]) => samplePoint(x, y, found)).filter((r) => r !== undefined);
182
+ const total = results.length, known = results.filter(Boolean);
183
+ const near = (centre) => known.filter((b) => distance(centre, b.lab) <= 0.035);
184
+ const seed = known.map((a) => near(a.lab)).sort((p, q) => q.length - p.length)[0];
185
+ const members = seed ? near(average(seed.map((m) => m.lab))) : [];
186
+ if (!members.length || members.length < 0.6 * total) return null;
187
+ const contra = members.filter((m) => m.contra).length, rgb = fromOklab(average(members.map((m) => m.lab)));
188
+ const against = known.filter((b) => !members.includes(b) && darkInk(fromOklab(b.lab)) !== darkInk(rgb)).length;
189
+ if ((contra >= 2 && contra >= members.length / 3) || (against >= 2 && against >= 0.2 * total)) return null;
190
+ const approx = members.filter((m) => m.approx).length / members.length;
191
+ return {
192
+ colour: rgb.map((v) => Math.round(v * 255)),
193
+ confidence: +((members.length / total) * (1 - 0.3 * approx) * (1 - contra / members.length)).toFixed(3),
194
+ scheme: darkInk(rgb) ? 'light' : 'dark',
195
+ };
196
+ }
197
+
198
+ // Bevel's surface and its weakest text ink, per scheme. A surface with its own
199
+ // palette (the page menu's stylesheet) passes its own pair.
200
+ export const BEVEL_SURFACE = { light: [0xFF, 0xFC, 0xF6], dark: [0x11, 0x13, 0x1E], mutedLight: [0x6B, 0x59, 0x42], mutedDark: [0x8A, 0x90, 0xAB] };
201
+
202
+ /**
203
+ * The look for UI drawn over `rect`: { scheme, surface }. `scheme` is the detected
204
+ * one, or pageScheme() when the colour is unknown. `surface` is Bevel's surface mixed
205
+ * up to 35% toward a confidently detected colour, never so far that the muted ink
206
+ * drops below 4.5:1, or null to keep Bevel's own fill. A page the detector cannot
207
+ * read is unknown, never a reason for the UI drawing over it to fail.
208
+ */
209
+ export function uiLook(rect, base = BEVEL_SURFACE) {
210
+ let found = null;
211
+ try {
212
+ found = pageColour(rect);
213
+ } catch {}
214
+ if (!found || found.confidence < 0.5) return { scheme: pageScheme(), surface: null };
215
+ const { scheme } = found;
216
+ const own = base[scheme], muted = (scheme === 'dark' ? base.mutedDark : base.mutedLight).map((v) => v / 255);
217
+ let surface = null;
218
+ for (let w = 0.35; found.confidence >= 0.75 && !surface && w > 0.01; w -= 0.05) {
219
+ const mix = own.map((v, k) => Math.round(v + (found.colour[k] - v) * w));
220
+ if (contrast(mix.map((v) => v / 255), muted) >= 4.5) surface = `rgb(${mix.join(' ')})`;
221
+ }
222
+ return { scheme, surface };
223
+ }
224
+
225
+ /**
226
+ * Gives an inline-styled Bevel surface the look for where it sits: `color-scheme`
227
+ * always, and `background` set to the tint or back to `background` when there is
228
+ * none. Pass `background: null` to leave the fill alone (a surface whose fill means
229
+ * something, like an alarm).
230
+ */
231
+ // A Bevel button rebuilds its inline style on every hover and focus, keeping only what
232
+ // was pinned, so on a button the look is pinned. An element with no size yet (hidden,
233
+ // empty) takes the page's declared scheme, or keeps the look it has.
234
+ export function paintLook(el, { rect = el.getBoundingClientRect(), background = null } = {}) {
235
+ const sized = rect.width > 0 && rect.height > 0;
236
+ if (!sized && pageScheme() === 'light dark') return null;
237
+ const look = sized ? uiLook(rect) : { scheme: pageScheme(), surface: null };
238
+ const props = { 'color-scheme': look.scheme };
239
+ if (background !== null) props.background = look.surface || background;
240
+ if (typeof el.pin === 'function') el.pin(props);
241
+ else for (const [prop, value] of Object.entries(props)) el.style.setProperty(prop, value, 'important');
242
+ return look;
243
+ }
244
+
245
+ /**
246
+ * The same look for a surface styled by a stylesheet of Bevel variables (the page
247
+ * menu): `color-scheme`, and `--bevel-surface` when tinted, set inline on `el`.
248
+ */
249
+ export function lookAt(el, rect, base) {
250
+ const look = uiLook(rect, base);
251
+ el.style.setProperty('color-scheme', look.scheme, 'important');
252
+ if (look.surface) el.style.setProperty('--bevel-surface', look.surface, 'important');
253
+ else el.style.removeProperty('--bevel-surface');
254
+ return look;
255
+ }
256
+
257
+ // A fixed surface the page moves under: read again once scrolling or resizing settles,
258
+ // when html or body change attributes (a theme toggle), when the reader's scheme
259
+ // changes, when it moves to another corner and when it is shown again. While it has
260
+ // no size (hidden) it keeps its last look.
261
+ export function followLook(root, target, base) {
262
+ let timer = 0;
263
+ const refresh = () => {
264
+ timer = 0;
265
+ const rect = target.getBoundingClientRect();
266
+ if (!document.hidden && root.isConnected && rect.width && rect.height) lookAt(root, rect, base);
267
+ };
268
+ const soon = () => {
269
+ clearTimeout(timer);
270
+ timer = setTimeout(refresh, 150);
271
+ };
272
+ window.addEventListener('scroll', soon, { capture: true, passive: true });
273
+ window.addEventListener('resize', soon);
274
+ document.addEventListener('visibilitychange', soon);
275
+ window.matchMedia?.('(prefers-color-scheme: dark)').addEventListener?.('change', soon);
276
+ const watch = new MutationObserver(soon);
277
+ watch.observe(document.documentElement, { attributes: true });
278
+ watch.observe(document.body, { attributes: true });
279
+ watch.observe(root, { attributes: true, attributeFilter: ['data-corner', 'hidden'] });
280
+ refresh();
281
+ }
package/src/ui/toast.js CHANGED
@@ -4,8 +4,9 @@
4
4
  // The Hyperclay dashboard's toast (Bevel's React toast with the dashboard's overrides),
5
5
  // drawn inline: a 3px tone edge, a 16px Bevel glyph, 13.5px semibold text, a quiet close.
6
6
  import { set } from "../lib/hostile-css.js";
7
- import { bevelBox, bevelSurface, bevelText, bevelButton, pageScheme, protectIcon } from "./bevel-controls.js";
7
+ import { bevelBox, bevelSurface, bevelText, bevelButton, protectIcon, paintLook } from "./bevel-controls.js";
8
8
  import { TOKENS, RULES, GLYPHS } from "./bevel.js";
9
+ import { mountEditorUI } from "./editor-ui.js";
9
10
 
10
11
  const TONES = {
11
12
  success: { edge: TOKENS.teal, icon: GLYPHS.toastSuccess },
@@ -24,9 +25,11 @@ function motion() {
24
25
  }
25
26
 
26
27
  // Looked up on every call, never cached: a live-sync morph or a caller can empty the
27
- // body, and a detached stack would swallow every toast after it.
28
+ // body, and a detached stack would swallow every toast after it. The stack sits in its own
29
+ // shadow root: the inline !important rules already beat the page's stylesheets, and the
30
+ // root also keeps out what they cannot, such as a page's ::before content.
28
31
  function stack() {
29
- const existing = document.querySelector("[data-clay-toasts]");
32
+ const existing = document.querySelector('clay-editor-ui[data-clay-ui="toasts"]')?.shadowRoot.querySelector("[data-clay-toasts]");
30
33
  if (existing) return existing;
31
34
  const box = bevelBox("div", [
32
35
  "position:fixed", "top:18px", "right:18px", "z-index:9999",
@@ -35,7 +38,7 @@ function stack() {
35
38
  ]);
36
39
  box.setAttribute("data-clay-toasts", "");
37
40
  box.setAttribute("aria-live", "polite");
38
- document.body.append(box);
41
+ mountEditorUI({ name: "toasts", content: box });
39
42
  return box;
40
43
  }
41
44
 
@@ -44,7 +47,7 @@ function build(message, type, { detail = "", actions = [] } = {}) {
44
47
  const el = bevelSurface("div", [
45
48
  "display:flex", "align-items:flex-start", ...RULES.toast, "width:100%",
46
49
  `border-inline-start:3px solid ${tone.edge}`,
47
- "cursor:pointer", "pointer-events:auto", `color-scheme:${pageScheme()}`,
50
+ "cursor:pointer", "pointer-events:auto",
48
51
  ...HIDDEN, `transition:${motion()}`,
49
52
  ]);
50
53
  el.setAttribute("data-clay-toast", type);
@@ -99,6 +102,7 @@ function closeButton(onClick) {
99
102
 
100
103
  function reveal(el) {
101
104
  stack().append(el);
105
+ paintLook(el, { background: TOKENS.surface });
102
106
  setTimeout(() => {
103
107
  set(el, "opacity", "1");
104
108
  set(el, "transform", "none");
@@ -145,11 +149,16 @@ function toastPersistent(message, messageType = "warning", { detail = "", action
145
149
  reveal(el);
146
150
  }
147
151
 
148
- // Takes down the persistent toast showing this message, if one is up.
149
- function dismissPersistent(message) {
152
+ // Takes down the persistent toast showing this message, if one is up. `now` takes the
153
+ // node out at once: a session that is over has no one left to read the fade.
154
+ function dismissPersistent(message, { now = false } = {}) {
150
155
  const el = activePersistentToasts.get(message);
151
156
  if (!el) return;
152
157
  activePersistentToasts.delete(message);
158
+ if (now) {
159
+ el.remove();
160
+ return;
161
+ }
153
162
  dismiss(el);
154
163
  }
155
164