@panphora/clayjs 1.17.0 → 1.18.1

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 (467) 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 +72474 -62090
  11. package/dist/m/attrs/{save-freeze-MYNVXREN.js → save-freeze-LWGF6XUN.js} +1 -1
  12. package/dist/m/c-2GRUM77Q.js +30 -0
  13. package/dist/m/c-2KIEGNC4.js +1 -0
  14. package/dist/m/{c-AMSNPOCX.js → c-2KL3BD52.js} +1 -1
  15. package/dist/m/c-2ZZBC45O.js +1 -0
  16. package/dist/m/c-372N6H3B.js +1 -0
  17. package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
  18. package/dist/m/c-3LDGIBMN.js +1 -0
  19. package/dist/m/c-3O7OBDCM.js +1 -0
  20. package/dist/m/c-4ASQPRQL.js +1 -0
  21. package/dist/m/c-4TXNLX4K.js +6 -0
  22. package/dist/m/c-4YU3BR4D.js +6 -0
  23. package/dist/m/c-5734MF2P.js +1 -0
  24. package/dist/m/c-57XDY6HC.js +1 -0
  25. package/dist/m/{c-KAU6E43D.js → c-5B4YBZXC.js} +1 -1
  26. package/dist/m/c-5ONTYIUI.js +1 -0
  27. package/dist/m/c-5OOVFCJM.js +1 -0
  28. package/dist/m/{c-3XIRD4NU.js → c-5T6NZHNM.js} +1 -1
  29. package/dist/m/{c-74MGYWZF.js → c-5ZQV7KC4.js} +1 -1
  30. package/dist/m/{c-7OLLJPWP.js → c-6DB2OLRJ.js} +1 -1
  31. package/dist/m/c-6DNRCPNM.js +1 -0
  32. package/dist/m/c-6UGWFMQC.js +2 -0
  33. package/dist/m/c-7K4SCQB7.js +1 -0
  34. package/dist/m/c-7MG5RU63.js +1 -0
  35. package/dist/m/c-7PRJCA3E.js +1 -0
  36. package/dist/m/c-A6L3FMUS.js +1 -0
  37. package/dist/m/c-AMU3BPPR.js +4 -0
  38. package/dist/m/{c-Y5HBBIQ4.js → c-AMYEFA2E.js} +1 -1
  39. package/dist/m/c-AQYCG7QW.js +1 -0
  40. package/dist/m/c-ATKAOH23.js +1 -0
  41. package/dist/m/c-B5CS55WR.js +1 -0
  42. package/dist/m/c-B5UJQT7W.js +1 -0
  43. package/dist/m/c-B7J4K2AE.js +3 -0
  44. package/dist/m/c-BHZYSHW4.js +1 -0
  45. package/dist/m/{c-VHM5XQHK.js → c-BIJALSRD.js} +1 -1
  46. package/dist/m/c-BJ7YQ5QZ.js +1 -0
  47. package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
  48. package/dist/m/{c-VE6ZMYMC.js → c-BUAYFEV5.js} +1 -1
  49. package/dist/m/c-C6IPRG34.js +1 -0
  50. package/dist/m/{c-KU3ZFADW.js → c-CAS3NND4.js} +1 -1
  51. package/dist/m/{c-PS5ARVWR.js → c-CCQ25GFY.js} +1 -1
  52. package/dist/m/c-CJFDLD43.js +1 -0
  53. package/dist/m/c-COPTL5WB.js +1 -0
  54. package/dist/m/c-D3E4WJ4M.js +166 -0
  55. package/dist/m/c-D5IKEKXW.js +7 -0
  56. package/dist/m/c-DHBRA6DL.js +1 -0
  57. package/dist/m/c-DKLS74XB.js +72 -0
  58. package/dist/m/c-DVYGFTSK.js +3 -0
  59. package/dist/m/c-E3ZXARWA.js +1 -0
  60. package/dist/m/c-EK63G3CG.js +1 -0
  61. package/dist/m/c-ERQSNR6K.js +13 -0
  62. package/dist/m/c-EVYAISP3.js +1 -0
  63. package/dist/m/c-EXKHPOVC.js +3 -0
  64. package/dist/m/{c-QJ4OM3G6.js → c-FWKHM4E7.js} +1 -1
  65. package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
  66. package/dist/m/c-GNDL3TKB.js +1 -0
  67. package/dist/m/c-GRWMD6O5.js +1 -0
  68. package/dist/m/c-GRXHHFPO.js +1 -0
  69. package/dist/m/c-HG2C77BM.js +4 -0
  70. package/dist/m/c-HHGJBU4D.js +1 -0
  71. package/dist/m/c-HMV6P4RX.js +1 -0
  72. package/dist/m/c-HMYWIHFG.js +1 -0
  73. package/dist/m/c-HT2DKBV2.js +1 -0
  74. package/dist/m/c-I2SCCUEW.js +1 -0
  75. package/dist/m/{c-TJ6WOJL3.js → c-ICJYHABF.js} +2 -2
  76. package/dist/m/c-IMZCWYHR.js +7 -0
  77. package/dist/m/c-IVMD3FEM.js +1 -0
  78. package/dist/m/{c-WDC5GVKU.js → c-IVTRVENZ.js} +1 -1
  79. package/dist/m/c-JKZJIUIP.js +1 -0
  80. package/dist/m/{c-UDUUJPFZ.js → c-JNLX3RJB.js} +1 -1
  81. package/dist/m/c-K3ADYNLU.js +1 -0
  82. package/dist/m/c-KGHVFRR3.js +1 -0
  83. package/dist/m/{c-5QWQ7A5L.js → c-KJJPV77R.js} +1 -1
  84. package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
  85. package/dist/m/{c-EZU56NQV.js → c-KP64LT2A.js} +2 -2
  86. package/dist/m/c-KQ7E3PYY.js +1 -0
  87. package/dist/m/c-L3F5ZLIV.js +1 -0
  88. package/dist/m/{c-3RUU4TPX.js → c-L42OLXWM.js} +3 -2
  89. package/dist/m/c-L5VFATT6.js +1 -0
  90. package/dist/m/c-L7JL5TL3.js +3 -0
  91. package/dist/m/c-LKSL4AUJ.js +1 -0
  92. package/dist/m/c-M6FNQCTQ.js +1 -0
  93. package/dist/m/c-MCOOHCSG.js +1 -0
  94. package/dist/m/c-MHI3GYKF.js +1 -0
  95. package/dist/m/c-MWXFLW4J.js +1 -0
  96. package/dist/m/{c-5RJFTLSF.js → c-NEL7HQW3.js} +1 -1
  97. package/dist/m/{c-DCXOXBF3.js → c-NQJRVJ56.js} +1 -1
  98. package/dist/m/c-NWX3MFT4.js +1 -0
  99. package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
  100. package/dist/m/c-OKXI52N7.js +1 -0
  101. package/dist/m/{c-R3L7ZJGB.js → c-OPLUZR3A.js} +1 -1
  102. package/dist/m/{c-GSFIGKOP.js → c-OTMKN2OO.js} +1 -1
  103. package/dist/m/{c-6NC5I2TV.js → c-OYRZ7XG2.js} +1 -1
  104. package/dist/m/{c-EVCQ3BKA.js → c-P3OFYUMA.js} +1 -1
  105. package/dist/m/c-PLN76GF3.js +6 -0
  106. package/dist/m/c-PLP4JKFC.js +1 -0
  107. package/dist/m/c-Q26MME6N.js +1 -0
  108. package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
  109. package/dist/m/{c-FRLHSY7E.js → c-QIKT6SKM.js} +1 -1
  110. package/dist/m/c-QIXT3Q7C.js +28 -0
  111. package/dist/m/c-QO4HNGCQ.js +1 -0
  112. package/dist/m/{c-JU3NK25H.js → c-R27DWQKX.js} +1 -1
  113. package/dist/m/{c-RSMOODDU.js → c-R2XUODVY.js} +1 -1
  114. package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
  115. package/dist/m/c-RND3G2S2.js +1 -0
  116. package/dist/m/{c-Q622NS2Y.js → c-S2U6NJU4.js} +1 -1
  117. package/dist/m/c-SPZPGD3R.js +1 -0
  118. package/dist/m/c-SW4YEG2D.js +8 -0
  119. package/dist/m/c-T637T7SN.js +1 -0
  120. package/dist/m/c-TJAXGIRV.js +1 -0
  121. package/dist/m/{c-RYUXUUBX.js → c-TNEM3RNM.js} +1 -1
  122. package/dist/m/c-TTB5EK4S.js +32 -0
  123. package/dist/m/c-U7SHNWDH.js +1 -0
  124. package/dist/m/c-UDHIVLYH.js +20 -0
  125. package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
  126. package/dist/m/{c-QICP2YR5.js → c-UORXLSD3.js} +1 -1
  127. package/dist/m/c-UPD33ZPE.js +1 -0
  128. package/dist/m/c-URSMSFCB.js +1 -0
  129. package/dist/m/c-UY4QX42B.js +1 -0
  130. package/dist/m/c-V44KWI2L.js +4 -0
  131. package/dist/m/c-VILENZTD.js +1 -0
  132. package/dist/m/{c-HDDEEL77.js → c-VJYEVRYK.js} +1 -1
  133. package/dist/m/{c-SMBZG5X4.js → c-VR2BJ4AN.js} +1 -1
  134. package/dist/m/c-W4JCVLAB.js +1 -0
  135. package/dist/m/c-W6DQSHXI.js +135 -0
  136. package/dist/m/c-WBGWGOM7.js +1 -0
  137. package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
  138. package/dist/m/c-WGW764WX.js +1 -0
  139. package/dist/m/c-WMRJKQ2V.js +5 -0
  140. package/dist/m/c-WNTYKCDG.js +1 -0
  141. package/dist/m/c-WQWIRWO3.js +72 -0
  142. package/dist/m/c-X5LNHET5.js +1 -0
  143. package/dist/m/{c-I2ZATQCD.js → c-X5TSXZYJ.js} +2 -2
  144. package/dist/m/c-XGH4GS2T.js +1 -0
  145. package/dist/m/c-Y6F2FORL.js +1 -0
  146. package/dist/m/c-YBKHUAK7.js +10 -0
  147. package/dist/m/c-YBSS55KU.js +20 -0
  148. package/dist/m/c-YCLU4UNT.js +2 -0
  149. package/dist/m/c-YVZ2L7CE.js +1 -0
  150. package/dist/m/c-ZK4GIZH2.js +1 -0
  151. package/dist/m/c-ZRXJD4G6.js +1 -0
  152. package/dist/m/c-ZWBI4HYK.js +1 -0
  153. package/dist/m/core/{admin-attrs-QHOKPJ7W.js → admin-attrs-YY7T5FGU.js} +1 -1
  154. package/dist/m/core/autosave-S6OMNDCK.js +1 -0
  155. package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
  156. package/dist/m/core/{host-permission-FNXVEFNE.js → host-permission-6EVJGNPL.js} +1 -1
  157. package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
  158. package/dist/m/core/{persist-JFYRHMRJ.js → persist-UKXV5KLA.js} +1 -1
  159. package/dist/m/core/recovery-DRFZRJED.js +1 -0
  160. package/dist/m/core/save-LYY7K6U7.js +1 -0
  161. package/dist/m/core/save-core-MKRPSEEO.js +1 -0
  162. package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
  163. package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
  164. package/dist/m/internals/{index-PVXGPUDA.js → index-TGLQINN3.js} +1 -1
  165. package/dist/m/loader-3BBNZZGM.js +1 -0
  166. package/dist/m/manifest-26C55578.js +1 -0
  167. package/dist/m/manifest-edit-4396A009.js +1 -0
  168. package/dist/m/manifest.json +2111 -1573
  169. package/dist/m/page/{editor-NNJA4OKD.js → editor-6LRB3GE4.js} +123 -21
  170. package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
  171. package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
  172. package/dist/m/plugins/{demo-RM5IR6AO.js → demo-OYO5GCAO.js} +1 -1
  173. package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
  174. package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
  175. package/dist/m/plugins/live-AD4H6XSL.js +1 -0
  176. package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
  177. package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
  178. package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
  179. package/dist/m/plugins/{people-SGPJKANK.js → people-JCHVPF7P.js} +1 -1
  180. package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
  181. package/dist/m/plugins/{richclay-NRQDSWX6.js → richclay-KLGETK77.js} +2 -2
  182. package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
  183. package/dist/m/plugins/source-TJXOIOYY.js +1 -0
  184. package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
  185. package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
  186. package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
  187. package/dist/m/plugins/{upload-WLYKK6TL.js → upload-NHGJXZK2.js} +1 -1
  188. package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
  189. package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
  190. package/dist/m/plugins/{wire-4N6GYAF6.js → wire-VBWXEXU2.js} +1 -1
  191. package/dist/m/reader-core-4W2KYEWQ.js +77 -0
  192. package/dist/m/route/route-62CHXL5B.js +1 -0
  193. package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
  194. package/dist/m/ui/index-IVWN2QC4.js +1 -0
  195. package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
  196. package/dist/offline-manifest.json +1367 -930
  197. package/dist/page/themes/light-1.css +8 -7
  198. package/dist/server/core.cjs +1440 -288
  199. package/dist/server/offline-runtime.cjs +63 -63
  200. package/dist/server/offline-runtime.mjs +63 -63
  201. package/dist/server/page-compaction-worker.cjs +3710 -0
  202. package/dist/server/rooms.mjs +5 -0
  203. package/docs/guide.md +653 -101
  204. package/entries/sap.js +5 -5
  205. package/package.json +1 -1
  206. package/src/core/admin-contenteditable.js +5 -2
  207. package/src/core/admin-inputs.js +1 -0
  208. package/src/core/admin-onclick.js +2 -1
  209. package/src/core/conflict-notice-ui.js +4 -7
  210. package/src/core/download.js +2 -2
  211. package/src/core/host-meta.js +38 -0
  212. package/src/core/is-edit-mode.js +17 -1
  213. package/src/core/mode-key.js +125 -19
  214. package/src/core/mode-support.js +5 -0
  215. package/src/core/mode-transition.js +20 -6
  216. package/src/core/preserve-authored.js +4 -4
  217. package/src/core/recovery.js +121 -33
  218. package/src/core/save-accepted.js +33 -0
  219. package/src/core/save-core.js +106 -31
  220. package/src/core/save-job.js +108 -0
  221. package/src/core/save-prepare.js +53 -0
  222. package/src/core/save-size-notice.js +3 -1
  223. package/src/core/save-slices.js +15 -0
  224. package/src/core/save.js +557 -127
  225. package/src/core/serialize-steps.js +109 -0
  226. package/src/core/snapshot.js +170 -39
  227. package/src/core/source-map.js +83 -38
  228. package/src/core/stale-host-notice.js +1 -0
  229. package/src/core/upgrade-run.js +59 -8
  230. package/src/dom/active-element.js +5 -0
  231. package/src/lib/assets.js +1 -0
  232. package/src/lib/slice.js +65 -0
  233. package/src/lib/uimotion.js +774 -0
  234. package/src/lib/unsaved-state.js +2 -2
  235. package/src/live/fast-lane.js +10 -3
  236. package/src/live/local-lane.js +202 -11
  237. package/src/live/ops.js +16 -0
  238. package/src/live/page-compaction.js +162 -0
  239. package/src/live/server-lane.js +1 -0
  240. package/src/live/store.js +11 -3
  241. package/src/loader-logic.js +3 -1
  242. package/src/loader.js +14 -4
  243. package/src/page/adopt-boot.js +2 -1
  244. package/src/page/awareness.js +33 -2
  245. package/src/page/blocks.js +89 -27
  246. package/src/page/boot-pack.js +1 -1
  247. package/src/page/chart-tables.js +21 -3
  248. package/src/page/chart.js +406 -63
  249. package/src/page/checklist.js +10 -135
  250. package/src/page/checkpoint-proof.js +7 -1
  251. package/src/page/chrome.js +250 -51
  252. package/src/page/clipboard-shape.js +126 -0
  253. package/src/page/drag.js +33 -3
  254. package/src/page/editor.js +136 -25
  255. package/src/page/export-assets.js +357 -0
  256. package/src/page/export-html.js +209 -0
  257. package/src/page/export-markdown.js +361 -0
  258. package/src/page/features/checklist.js +428 -0
  259. package/src/page/features/export-html.js +189 -0
  260. package/src/page/features/export-markdown.js +169 -0
  261. package/src/page/features/import-markdown.js +349 -0
  262. package/src/page/features/media.js +61 -5
  263. package/src/page/features/registry.js +18 -0
  264. package/src/page/features/table.js +373 -83
  265. package/src/page/features/title-cover.js +331 -0
  266. package/src/page/features/title-icon.js +213 -0
  267. package/src/page/features/toc.js +87 -0
  268. package/src/page/format-command.js +25 -1
  269. package/src/page/format-gate.js +68 -18
  270. package/src/page/format-host.js +17 -0
  271. package/src/page/format-native-commands.js +21 -5
  272. package/src/page/image-actions.js +64 -0
  273. package/src/page/import-markdown.js +262 -0
  274. package/src/page/items.js +135 -17
  275. package/src/page/markdown.js +6 -2
  276. package/src/page/media.js +35 -119
  277. package/src/page/placeholder.js +14 -1
  278. package/src/page/projection.js +98 -10
  279. package/src/page/recover.js +10 -12
  280. package/src/page/recovery-ui.js +464 -0
  281. package/src/page/rich/chart.js +63 -7
  282. package/src/page/save-projection.js +28 -0
  283. package/src/page/save-states.js +13 -3
  284. package/src/page/schema.js +19 -3
  285. package/src/page/table-paste.js +3 -1
  286. package/src/page/table-style.js +4 -4
  287. package/src/page/table.js +44 -2
  288. package/src/page/themes/light-1.css +8 -7
  289. package/src/page/themes/light-1.source.css +8 -7
  290. package/src/page/toc-link.js +31 -0
  291. package/src/page/toc.js +5 -0
  292. package/src/page/view-hooks.js +38 -12
  293. package/src/page/view.js +13 -0
  294. package/src/page/ybind/binding.js +88 -5
  295. package/src/page/ybind/bridge.js +187 -20
  296. package/src/page/ybind/containment.js +162 -0
  297. package/src/page/ybind/doc.js +17 -9
  298. package/src/page/ybind/file-debt.js +63 -0
  299. package/src/page/ybind/layout.js +7 -2
  300. package/src/page/ybind/marks.js +1 -1
  301. package/src/page/ybind/moves.js +5 -4
  302. package/src/page/ybind/origins.js +106 -4
  303. package/src/page/ybind/parts.js +8 -1
  304. package/src/page/ybind/provider.js +113 -17
  305. package/src/page/ybind/recovery-proof.js +32 -9
  306. package/src/page/ybind/recovery-replay.js +558 -15
  307. package/src/page/ybind/recovery.js +174 -45
  308. package/src/page/ybind/table.js +67 -11
  309. package/src/page/ybind/walk-delta.js +258 -0
  310. package/src/page/ybind/walk.js +54 -29
  311. package/src/page/ybind/writer.js +1 -1
  312. package/src/plugins/ai-edit.js +13 -9
  313. package/src/plugins/indicator-chip.js +91 -0
  314. package/src/plugins/indicator.js +13 -80
  315. package/src/plugins/live.js +89 -14
  316. package/src/plugins/menu/actions.js +196 -0
  317. package/src/plugins/menu/people.js +279 -0
  318. package/src/plugins/menu/placement.js +121 -0
  319. package/src/plugins/menu/preferences.js +9 -0
  320. package/src/plugins/menu/seo.js +316 -397
  321. package/src/plugins/menu/style.js +171 -0
  322. package/src/plugins/menu/variants.js +296 -0
  323. package/src/plugins/menu.js +186 -183
  324. package/src/plugins/page-view.js +3 -2
  325. package/src/plugins/page.js +249 -44
  326. package/src/plugins/sortable.js +8 -1
  327. package/src/plugins/source.js +6 -0
  328. package/src/plugins/upgrade.js +51 -17
  329. package/src/plugins/upload-ui.js +12 -17
  330. package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
  331. package/src/plugins/visitor-drafts/intro-style.js +7 -2
  332. package/src/plugins/visitor-drafts/intro.js +5 -3
  333. package/src/plugins/visitor-drafts/owner-chip.js +8 -12
  334. package/src/plugins/visitor-drafts/prompt.js +11 -9
  335. package/src/plugins/visitor-drafts/source.js +4 -3
  336. package/src/plugins/visitor-drafts/store.js +138 -9
  337. package/src/plugins/visitor-drafts/ui-memory.js +2 -2
  338. package/src/plugins/visitor-drafts-owner.js +4 -6
  339. package/src/plugins/visitor-drafts.js +5 -3
  340. package/src/recovery/store.js +1 -1
  341. package/src/route/route.js +4 -0
  342. package/src/server/rooms/index.js +5 -1
  343. package/src/server/rooms/live-data-rooms.js +302 -50
  344. package/src/server/rooms/page-compaction-worker.js +12 -0
  345. package/src/server/rooms/page-compactor.js +92 -0
  346. package/src/server/rooms/page-room-conformance.js +461 -0
  347. package/src/source/parse-steps.js +94 -0
  348. package/src/source/session.js +233 -57
  349. package/src/sync/editing-sender.js +12 -0
  350. package/src/sync/live-sync-edit.js +22 -2
  351. package/src/sync/live-sync.js +150 -23
  352. package/src/sync/page-relay.js +52 -0
  353. package/src/sync/presence-store.js +44 -4
  354. package/src/sync/presence.js +6 -3
  355. package/src/sync/saved-stream.js +11 -6
  356. package/src/ui/bevel-controls.js +1 -0
  357. package/src/ui/bevel-dialog.js +5 -2
  358. package/src/ui/editor-ui.js +77 -0
  359. package/src/ui/page-colour.js +281 -0
  360. package/src/ui/toast.js +16 -7
  361. package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
  362. package/src/vendor/hyper-undo.vendor.js +1 -1
  363. package/src/vendor/hypercms.vendor.js +9 -9
  364. package/src/vendor/parse5.vendor.js +2 -2
  365. package/src/vendor/sap-core.vendor.js +5 -5
  366. package/dist/m/c-2SPVQSHC.js +0 -1
  367. package/dist/m/c-336SGU5W.js +0 -1
  368. package/dist/m/c-3H6BZDNJ.js +0 -6
  369. package/dist/m/c-4DFEJ2PO.js +0 -3
  370. package/dist/m/c-56QRZTZH.js +0 -1
  371. package/dist/m/c-5J4E7O4Y.js +0 -4
  372. package/dist/m/c-5LKDWQTL.js +0 -19
  373. package/dist/m/c-5V3MOVJT.js +0 -1
  374. package/dist/m/c-6AZOGAPE.js +0 -1
  375. package/dist/m/c-6CRUUXOX.js +0 -5
  376. package/dist/m/c-6HMB24EV.js +0 -1
  377. package/dist/m/c-6KEVEQ7L.js +0 -1
  378. package/dist/m/c-A3E3NIMC.js +0 -1
  379. package/dist/m/c-BQ6LOBEE.js +0 -1
  380. package/dist/m/c-BTDI4Y23.js +0 -1
  381. package/dist/m/c-CJ3A7ZKE.js +0 -1
  382. package/dist/m/c-CQHPGKX2.js +0 -1
  383. package/dist/m/c-DFS4XZJV.js +0 -2
  384. package/dist/m/c-DNENWY4O.js +0 -1
  385. package/dist/m/c-E4LIR5X5.js +0 -1
  386. package/dist/m/c-FDNHHZY5.js +0 -1
  387. package/dist/m/c-FROJUZMV.js +0 -1
  388. package/dist/m/c-G2JPKCTP.js +0 -1
  389. package/dist/m/c-GB6TU7WV.js +0 -1
  390. package/dist/m/c-GFJJEBTU.js +0 -1
  391. package/dist/m/c-GJAEGGXY.js +0 -1
  392. package/dist/m/c-GOWBDM6J.js +0 -1
  393. package/dist/m/c-HFKUVACH.js +0 -1
  394. package/dist/m/c-HHDFCNHV.js +0 -1
  395. package/dist/m/c-IEM4NTCS.js +0 -1
  396. package/dist/m/c-JDCX26UP.js +0 -25
  397. package/dist/m/c-KGVGROZT.js +0 -1
  398. package/dist/m/c-KQ6TYJAP.js +0 -135
  399. package/dist/m/c-LFPHCXY3.js +0 -1
  400. package/dist/m/c-LRPUO2MX.js +0 -1
  401. package/dist/m/c-LWLBHLVC.js +0 -3
  402. package/dist/m/c-M2L47S2X.js +0 -7
  403. package/dist/m/c-MFP2ZWD4.js +0 -1
  404. package/dist/m/c-MKSQ4Y57.js +0 -1
  405. package/dist/m/c-NUFFJVVJ.js +0 -1
  406. package/dist/m/c-NUHSPT5P.js +0 -1
  407. package/dist/m/c-O72HVYSA.js +0 -1
  408. package/dist/m/c-OIBUTDXI.js +0 -1
  409. package/dist/m/c-OW5QIGG3.js +0 -1
  410. package/dist/m/c-PGRCK73F.js +0 -1
  411. package/dist/m/c-Q7NQD65V.js +0 -1
  412. package/dist/m/c-QH63LQOS.js +0 -1
  413. package/dist/m/c-R7IWVINQ.js +0 -1
  414. package/dist/m/c-RM7J7BV2.js +0 -1
  415. package/dist/m/c-S4ZEH46B.js +0 -9
  416. package/dist/m/c-SDAXDM2D.js +0 -1
  417. package/dist/m/c-SMGU7RT3.js +0 -235
  418. package/dist/m/c-SPIORRYS.js +0 -1
  419. package/dist/m/c-SWLRT66R.js +0 -1
  420. package/dist/m/c-TCJTVAFN.js +0 -1
  421. package/dist/m/c-TQEMVJCS.js +0 -1
  422. package/dist/m/c-TYLXX4NF.js +0 -21
  423. package/dist/m/c-U62OB4BT.js +0 -1
  424. package/dist/m/c-U6YWUAR3.js +0 -8
  425. package/dist/m/c-UDCSU5G5.js +0 -2
  426. package/dist/m/c-VI5RMSVY.js +0 -1
  427. package/dist/m/c-VRZN56XY.js +0 -1
  428. package/dist/m/c-W5MCHEGA.js +0 -1
  429. package/dist/m/c-WNYBLIOZ.js +0 -1
  430. package/dist/m/c-WWXIDH4O.js +0 -28
  431. package/dist/m/c-XCNTK76U.js +0 -1
  432. package/dist/m/c-XMS6DPBR.js +0 -1
  433. package/dist/m/c-ZUCOKOHT.js +0 -1
  434. package/dist/m/c-ZVPVXTJF.js +0 -4
  435. package/dist/m/c-ZVRKXKM7.js +0 -1
  436. package/dist/m/core/autosave-HRYGUWV6.js +0 -1
  437. package/dist/m/core/conflict-notice-6MROJN2L.js +0 -1
  438. package/dist/m/core/is-edit-mode-KTWA3O5N.js +0 -1
  439. package/dist/m/core/recovery-FLTCC2OC.js +0 -1
  440. package/dist/m/core/save-HQZ4S667.js +0 -1
  441. package/dist/m/core/save-core-KGQWFUNH.js +0 -1
  442. package/dist/m/core/snapshot-EGQZNT45.js +0 -1
  443. package/dist/m/core/unsaved-warning-IDJLD66H.js +0 -1
  444. package/dist/m/loader-S4I7GKXR.js +0 -1
  445. package/dist/m/manifest-4C419D67.js +0 -1
  446. package/dist/m/manifest-edit-B077F288.js +0 -1
  447. package/dist/m/plugins/ai-edit-QN4PMMNL.js +0 -6
  448. package/dist/m/plugins/indicator-URDBBOQO.js +0 -1
  449. package/dist/m/plugins/live-HFWD4OIH.js +0 -1
  450. package/dist/m/plugins/menu-3HIEQSI6.js +0 -138
  451. package/dist/m/plugins/page-JOKB6XPR.js +0 -2
  452. package/dist/m/plugins/page-view-NXOJHNW6.js +0 -1
  453. package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
  454. package/dist/m/plugins/sortable-GWT4PWDV.js +0 -1
  455. package/dist/m/plugins/source-4ZRSAIYM.js +0 -1
  456. package/dist/m/plugins/undo-VDA5P7FH.js +0 -1
  457. package/dist/m/plugins/upgrade-W7FG5VFG.js +0 -1
  458. package/dist/m/plugins/visitor-drafts-LIDY7NH6.js +0 -21
  459. package/dist/m/plugins/visitor-drafts-owner-ZXCKBZE7.js +0 -1
  460. package/dist/m/reader-core-DXYCURFF.js +0 -68
  461. package/dist/m/route/route-KOCZSEGN.js +0 -1
  462. package/dist/m/sync/live-sync-J7VMYTKS.js +0 -1
  463. package/dist/m/ui/index-3JLVI27O.js +0 -1
  464. package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
  465. /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
  466. /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
  467. /package/dist/m/plugins/{richclay-NRQDSWX6.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
@@ -0,0 +1,428 @@
1
+ // The checklist's decoration: the owner and due chips on every check item. It
2
+ // loads the first time a document holds or takes one, through the view hooks, so a
3
+ // document with no checklist never fetches it. The keymap stays in checklist.js.
4
+ import { view as pmView } from "../../vendor/prosemirror.vendor.js";
5
+ import { schema } from "../schema.js";
6
+ import { asStep } from "../blocksel.js";
7
+ import { addViewHook } from "../view-hooks.js";
8
+
9
+ const { Decoration, DecorationSet } = pmView;
10
+
11
+ // The owner chips draw the same initials face a board card does. These two are the
12
+ // parts of that face this module needs, kept here so the checklist part does not
13
+ // share a chunk with the board's parts and cost the board its one request.
14
+ const el = (doc, tag, cls, attrs = {}, text = null) => {
15
+ const node = doc.createElement(tag);
16
+ if (cls) node.className = cls;
17
+ for (const [key, value] of Object.entries(attrs)) if (value !== null && value !== undefined && value !== false) node.setAttribute(key, value === true ? "" : String(value));
18
+ if (text !== null) node.textContent = text;
19
+ return node;
20
+ };
21
+
22
+ const cardFace = (doc, name) => el(doc, "span", "page-board-face", { "aria-hidden": "true" }, ([...String(name).trim()][0] ?? "?").toUpperCase());
23
+
24
+ function itemAt(view, id) {
25
+ let found = null;
26
+ view.state.doc.forEach((node, pos) => { if (node.attrs.id === id && node.type === schema.nodes.list_item && node.attrs.kind === "check") found = { node, pos }; });
27
+ return found;
28
+ }
29
+
30
+ function setItem(view, id, attr, value) {
31
+ const found = itemAt(view, id);
32
+ if (!found) return;
33
+ view.dispatch(asStep(view.state.tr.setNodeMarkup(found.pos, null, { ...found.node.attrs, [attr]: value || null })));
34
+ }
35
+
36
+ // The design's three line icons, at the editor's 16px stroke-1.5 drawing.
37
+ const ICONS = {
38
+ person: '<circle cx="8" cy="5.4" r="2.3"/><path d="M3.6 13.2c.6-2.1 2.2-3.3 4.4-3.3s3.8 1.2 4.4 3.3"/>',
39
+ calendar: '<rect x="2.5" y="3.5" width="11" height="10"/><path d="M2.5 6.8h11M5.5 2v2.4M10.5 2v2.4"/>',
40
+ clock: '<circle cx="8" cy="8" r="5.5"/><path d="M8 4.8v3.4l2.3 1.4"/>',
41
+ tick: '<path d="M3.5 8.3l3 3 6-6.2"/>',
42
+ plus: '<path d="M8 3.5v9M3.5 8h9"/>',
43
+ clear: '<path d="M4.8 4.8l6.4 6.4M11.2 4.8l-6.4 6.4"/>',
44
+ "chevron-left": '<path d="M9.8 3.6L5.4 8l4.4 4.4"/>',
45
+ "chevron-right": '<path d="M6.2 3.6L10.6 8l-4.4 4.4"/>',
46
+ };
47
+
48
+ function icon(doc, paths) {
49
+ const node = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
50
+ node.setAttribute("viewBox", "0 0 16 16");
51
+ node.setAttribute("width", "14");
52
+ node.setAttribute("height", "14");
53
+ node.setAttribute("fill", "none");
54
+ node.setAttribute("stroke", "currentColor");
55
+ node.setAttribute("stroke-width", "1.5");
56
+ node.setAttribute("stroke-linecap", "round");
57
+ node.setAttribute("stroke-linejoin", "round");
58
+ node.setAttribute("aria-hidden", "true");
59
+ node.innerHTML = paths;
60
+ return node;
61
+ }
62
+
63
+ // A tag's words. Today, tomorrow and a short date for the day, as a board card
64
+ // reads its own due badge (board-view.js), with the one word a tag needs that the
65
+ // badge never shows.
66
+ const pad = (n) => String(n).padStart(2, "0");
67
+ const DUE = /^\d{4}-\d{2}-\d{2}$/;
68
+ const todayKey = (now = new Date()) => `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
69
+ const dayAfter = (key) => {
70
+ const day = new Date(`${key}T00:00:00Z`);
71
+ day.setUTCDate(day.getUTCDate() + 1);
72
+ return day.toISOString().slice(0, 10);
73
+ };
74
+ function dueLabel(due, today) {
75
+ if (!due) return "No date";
76
+ if (!DUE.test(due)) return due;
77
+ if (due === today) return "Today";
78
+ if (due === dayAfter(today)) return "Tomorrow";
79
+ const sameYear = due.slice(0, 4) === today.slice(0, 4);
80
+ return new Date(`${due}T00:00:00Z`).toLocaleDateString("en-US", { month: "short", day: "numeric", ...(sameYear ? {} : { year: "numeric" }), timeZone: "UTC" });
81
+ }
82
+
83
+ function tag(doc, { name, paths, label, aria, filled, overdue = false }) {
84
+ const button = el(doc, "button", "page-check-chip", {
85
+ type: "button",
86
+ "data-check": name,
87
+ "data-filled": String(filled),
88
+ "data-overdue": overdue ? "true" : null,
89
+ "aria-expanded": "false",
90
+ "aria-label": aria,
91
+ });
92
+ button.append(icon(doc, paths), el(doc, "span", "page-check-chip-label", {}, label));
93
+ return button;
94
+ }
95
+
96
+ // The floating panels: one is open at a time, and it hangs off the tag that opened
97
+ // it, right edges aligned, flipping above the tag when there is no room below. Escape
98
+ // closes it and hands focus back to the tag; a resize or a scroll moves it with the
99
+ // tag; a mousedown outside closes it.
100
+ let openPopup = null;
101
+
102
+ function popup(view, anchor, { className, label, check, onKey = null }) {
103
+ const doc = anchor.ownerDocument;
104
+ const win = doc.defaultView;
105
+ if (openPopup) openPopup.close();
106
+ for (const node of doc.querySelectorAll(".page-check-popup")) node.remove();
107
+ const parent = view.dom.parentNode;
108
+ const anchored = ["relative", "absolute", "fixed", "sticky"].includes(win?.getComputedStyle?.(parent)?.position ?? "");
109
+ const placement = parent && anchored ? parent : doc.body;
110
+ const panel = el(doc, "div", `page-check-popup ${className}`, { role: "dialog", "aria-label": label, "data-check": check });
111
+ let closed = false;
112
+ const place = () => {
113
+ const ring = anchor.getBoundingClientRect();
114
+ const base = placement === doc.body ? { top: -(win?.scrollY ?? 0), left: -(win?.scrollX ?? 0) } : placement.getBoundingClientRect();
115
+ const box = panel.getBoundingClientRect();
116
+ const width = box.width || Math.min(264, (win?.innerWidth || 292) - 28);
117
+ const room = (win?.innerHeight ?? 0) - ring.bottom - 2;
118
+ const flip = box.height > 0 && room < box.height;
119
+ panel.style.left = `${Math.round(ring.right - width - base.left)}px`;
120
+ panel.style.top = `${Math.round((flip ? ring.top - box.height - 2 : ring.bottom + 2) - base.top)}px`;
121
+ };
122
+ const outside = (event) => {
123
+ if (!panel.isConnected) return close();
124
+ if (panel.contains(event.target) || anchor.contains(event.target)) return;
125
+ close();
126
+ };
127
+ const close = ({ focusAnchor = false } = {}) => {
128
+ if (closed) return;
129
+ closed = true;
130
+ doc.removeEventListener("mousedown", outside, true);
131
+ win?.removeEventListener("resize", place);
132
+ win?.removeEventListener("scroll", place, true);
133
+ if (openPopup?.panel === panel) openPopup = null;
134
+ panel.remove();
135
+ anchor.setAttribute("aria-expanded", "false");
136
+ const fromKeys = anchor.dataset.fromKeys === "true";
137
+ delete anchor.dataset.fromKeys;
138
+ if (focusAnchor && fromKeys) view.focus();
139
+ else if (focusAnchor) anchor.focus();
140
+ };
141
+ panel.addEventListener("keydown", (event) => {
142
+ event.stopPropagation();
143
+ if (event.key === "Escape") { event.preventDefault(); close({ focusAnchor: true }); return; }
144
+ onKey?.(event, panel);
145
+ });
146
+ placement.append(panel);
147
+ anchor.setAttribute("aria-expanded", "true");
148
+ openPopup = { panel, close: () => close() };
149
+ doc.addEventListener("mousedown", outside, true);
150
+ win?.addEventListener("resize", place);
151
+ win?.addEventListener("scroll", place, true);
152
+ return { panel, close, place };
153
+ }
154
+
155
+ function peopleIn(view, extra) {
156
+ const found = new Map();
157
+ view.state.doc.forEach((node) => {
158
+ if (node.type !== schema.nodes.list_item || node.attrs.kind !== "check") return;
159
+ const name = String(node.attrs.owner ?? "").trim();
160
+ if (!name) return;
161
+ const key = name.toLowerCase();
162
+ const entry = found.get(key) ?? { name, count: 0 };
163
+ entry.count += 1;
164
+ found.set(key, entry);
165
+ });
166
+ for (const person of extra ?? []) {
167
+ const name = String((typeof person === "string" ? person : person?.name) ?? "").trim();
168
+ if (name && !found.has(name.toLowerCase())) found.set(name.toLowerCase(), { name, count: 0 });
169
+ }
170
+ return [...found.values()].sort((a, b) => a.name.localeCompare(b.name));
171
+ }
172
+
173
+ // The assignee panel: the document's people with their check-item counts, a name
174
+ // box for anyone new, and a row that clears the owner.
175
+ function ownerPicker(view, id, anchor) {
176
+ const doc = anchor.ownerDocument;
177
+ const current = itemAt(view, id)?.node.attrs.owner || "";
178
+ const { panel, close, place } = popup(view, anchor, {
179
+ className: "page-check-owner",
180
+ label: "Assignee",
181
+ check: "owner-panel",
182
+ onKey: (event, panel) => {
183
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
184
+ event.preventDefault();
185
+ const stops = [...panel.querySelectorAll("input, button:not([disabled])")];
186
+ const at = stops.indexOf(doc.activeElement);
187
+ const step = event.key === "ArrowDown" ? 1 : -1;
188
+ const next = at < 0 ? (step > 0 ? stops[0] : stops[stops.length - 1]) : stops[(at + step + stops.length) % stops.length];
189
+ next?.focus();
190
+ },
191
+ });
192
+ const input = el(doc, "input", "page-check-search", { type: "text", placeholder: "Find or type a name", "aria-label": "Find or type a name" });
193
+ const list = el(doc, "div", "page-check-people");
194
+ const remove = el(doc, "button", "page-check-person page-check-danger", { type: "button", "data-check": "clear-owner" });
195
+ remove.append(icon(doc, ICONS.clear), el(doc, "span", "page-check-person-name", {}, "Remove assignee"));
196
+ remove.disabled = !current;
197
+ remove.addEventListener("click", () => choose(null));
198
+ panel.append(input, el(doc, "div", "page-check-subhead", {}, "People in this document"), list, el(doc, "div", "page-check-divider"), remove);
199
+ let available = [];
200
+ const choose = (name) => { close(); setItem(view, id, "owner", name); };
201
+ const personRow = (name, count) => {
202
+ const selected = name.toLowerCase() === String(current).toLowerCase();
203
+ const row = el(doc, "button", "page-check-person", { type: "button", "data-check": "owner-option", "data-name": name, "aria-pressed": String(selected) });
204
+ row.append(cardFace(doc, name), el(doc, "span", "page-check-person-name", {}, name), el(doc, "span", "page-check-person-meta", {}, `${count} ${count === 1 ? "item" : "items"}`));
205
+ if (selected) row.append(icon(doc, ICONS.tick));
206
+ row.addEventListener("click", () => choose(name));
207
+ return row;
208
+ };
209
+ const addRow = (name) => {
210
+ const row = el(doc, "button", "page-check-person", { type: "button", "data-check": "owner-new", "data-name": name });
211
+ const label = el(doc, "span", "page-check-person-name", {}, "Assign to ");
212
+ label.append(el(doc, "strong", null, {}, name));
213
+ row.append(icon(doc, ICONS.plus), label, el(doc, "span", "page-check-person-meta", {}, "New"));
214
+ row.addEventListener("click", () => choose(name));
215
+ return row;
216
+ };
217
+ const draw = () => {
218
+ const typed = input.value.trim();
219
+ const needle = typed.toLowerCase();
220
+ const people = peopleIn(view, available);
221
+ const rows = [];
222
+ if (typed && !people.some((person) => person.name.toLowerCase() === needle)) rows.push(addRow(typed));
223
+ for (const person of people) {
224
+ if (needle && !person.name.toLowerCase().includes(needle)) continue;
225
+ rows.push(personRow(person.name, person.count));
226
+ }
227
+ list.replaceChildren(...rows);
228
+ };
229
+ input.addEventListener("keydown", (event) => {
230
+ event.stopPropagation();
231
+ if (event.key === "Escape") { event.preventDefault(); close({ focusAnchor: true }); return; }
232
+ if (event.key !== "Enter") return;
233
+ event.preventDefault();
234
+ list.querySelector("button[data-check]")?.click();
235
+ });
236
+ input.addEventListener("input", draw);
237
+ draw();
238
+ place();
239
+ input.focus();
240
+ const source = globalThis.clay?.people?.available;
241
+ if (source) Promise.resolve().then(() => source()).then((names) => { if (!openPopup || openPopup.panel !== panel) return; available = Array.isArray(names) ? names : []; draw(); }).catch(() => {});
242
+ }
243
+
244
+ // A day of the local calendar, its ISO key, and the words a quick row and a day
245
+ // button read it in.
246
+ const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
247
+ const dateOf = (year, month, day) => new Date(year, month, day);
248
+ const shortDay = (day) => day.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" });
249
+ const longDay = (day) => day.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" });
250
+ const dueDate = (key) => {
251
+ if (!key || !DUE.test(key)) return null;
252
+ const [year, month, day] = key.split("-").map(Number);
253
+ return dateOf(year, month - 1, day);
254
+ };
255
+
256
+ // The due date panel: three quick days, a compact month calendar, and a row that
257
+ // clears the date. The calendar opens on the month of the item's own date, or on
258
+ // this month when it has none.
259
+ function datePicker(view, id, anchor) {
260
+ const doc = anchor.ownerDocument;
261
+ const current = itemAt(view, id)?.node.attrs.due || null;
262
+ const today = todayKey();
263
+ const clock = new Date();
264
+ const now = dateOf(clock.getFullYear(), clock.getMonth(), clock.getDate());
265
+ const opened = dueDate(current) ?? now;
266
+ let year = opened.getFullYear();
267
+ let month = opened.getMonth();
268
+
269
+ const { panel, close, place } = popup(view, anchor, {
270
+ className: "page-check-date",
271
+ label: "Due date",
272
+ check: "date-panel",
273
+ onKey: (event, panel) => gridKey(event, panel),
274
+ });
275
+ const pick = (key) => { close(); setItem(view, id, "due", key); };
276
+
277
+ const quick = el(doc, "div", "page-check-quick");
278
+ const quickRow = (label, offset) => {
279
+ const day = dateOf(now.getFullYear(), now.getMonth(), now.getDate() + offset);
280
+ const row = el(doc, "button", "page-check-quick-row", { type: "button", "data-check": "date-quick", "data-date": todayKey(day) });
281
+ row.append(icon(doc, ICONS.calendar), el(doc, "span", "page-check-quick-label", {}, label), el(doc, "span", "page-check-quick-meta", {}, shortDay(day)));
282
+ row.addEventListener("click", () => pick(todayKey(day)));
283
+ return row;
284
+ };
285
+ quick.append(quickRow("Today", 0), quickRow("Tomorrow", 1), quickRow("Next week", 7));
286
+
287
+ const calendar = el(doc, "div", "page-check-cal");
288
+ const head = el(doc, "div", "page-check-cal-head");
289
+ const monthLabel = el(doc, "span", "page-check-cal-month");
290
+ const nav = el(doc, "span", "page-check-cal-nav");
291
+ const step = (by) => {
292
+ const moved = dateOf(year, month + by, 1);
293
+ year = moved.getFullYear();
294
+ month = moved.getMonth();
295
+ draw();
296
+ };
297
+ const previous = el(doc, "button", "page-check-cal-btn", { type: "button", "aria-label": "Previous month" });
298
+ const next = el(doc, "button", "page-check-cal-btn", { type: "button", "aria-label": "Next month" });
299
+ previous.append(icon(doc, ICONS["chevron-left"]));
300
+ next.append(icon(doc, ICONS["chevron-right"]));
301
+ previous.addEventListener("click", () => step(-1));
302
+ next.addEventListener("click", () => step(1));
303
+ nav.append(previous, next);
304
+ head.append(monthLabel, nav);
305
+ const grid = el(doc, "div", "page-check-cal-grid");
306
+ calendar.append(head, grid);
307
+
308
+ const draw = () => {
309
+ monthLabel.replaceChildren(el(doc, "span", null, {}, `${MONTHS[month]} `), el(doc, "span", "page-check-cal-year", {}, String(year)));
310
+ const first = dateOf(year, month, 1);
311
+ const days = dateOf(year, month + 1, 0).getDate();
312
+ const cells = Math.ceil((first.getDay() + days) / 7) * 7;
313
+ const heads = ["S", "M", "T", "W", "T", "F", "S"].map((letter) => el(doc, "span", "page-check-cal-dow", {}, letter));
314
+ const buttons = [];
315
+ for (let i = 0; i < cells; i += 1) {
316
+ const day = dateOf(year, month, 1 - first.getDay() + i);
317
+ const key = todayKey(day);
318
+ const button = el(doc, "button", "page-check-cal-day", {
319
+ type: "button",
320
+ "data-check": "date-day",
321
+ "data-date": key,
322
+ "data-outside": String(day.getMonth() !== month),
323
+ "data-today": String(key === today),
324
+ "aria-pressed": String(key === current),
325
+ "aria-label": longDay(day),
326
+ }, String(day.getDate()));
327
+ button.addEventListener("click", () => pick(key));
328
+ buttons.push(button);
329
+ }
330
+ grid.replaceChildren(...heads, ...buttons);
331
+ };
332
+
333
+ const gridKey = (event, panel) => {
334
+ const button = doc.activeElement?.closest?.('[data-check="date-day"]');
335
+ if (!button) return;
336
+ if (event.key === "Enter" || event.key === " ") { event.preventDefault(); button.click(); return; }
337
+ const move = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }[event.key];
338
+ if (!move) return;
339
+ event.preventDefault();
340
+ const from = dueDate(button.dataset.date);
341
+ const day = dateOf(from.getFullYear(), from.getMonth(), from.getDate() + move);
342
+ if (day.getMonth() !== month || day.getFullYear() !== year) {
343
+ year = day.getFullYear();
344
+ month = day.getMonth();
345
+ draw();
346
+ }
347
+ panel.querySelector(`[data-check="date-day"][data-date="${todayKey(day)}"]`)?.focus();
348
+ };
349
+
350
+ const clear = el(doc, "button", "page-check-quick-row page-check-danger", { type: "button", "data-check": "clear-date" });
351
+ clear.append(icon(doc, ICONS.clear), el(doc, "span", "page-check-quick-label", {}, "Clear date"));
352
+ clear.disabled = !current;
353
+ clear.addEventListener("click", () => pick(null));
354
+
355
+ panel.append(quick, calendar, el(doc, "div", "page-check-divider"), clear);
356
+ draw();
357
+ place();
358
+ (grid.querySelector('[data-check="date-day"][aria-pressed="true"]') ?? grid.querySelector('[data-check="date-day"][data-today="true"]'))?.focus();
359
+ }
360
+
361
+ function controls(view, node) {
362
+ const doc = view.dom.ownerDocument;
363
+ const box = doc.createElement("span");
364
+ box.className = "page-check-controls";
365
+ box.contentEditable = "false";
366
+ box.dataset.empty = String(!node.attrs.owner && !node.attrs.due);
367
+ const today = todayKey();
368
+ const ownerName = node.attrs.owner || "Unassigned";
369
+ const owner = tag(doc, { name: "owner", paths: ICONS.person, label: ownerName, aria: `Assignee: ${ownerName}`, filled: !!node.attrs.owner });
370
+ const dateName = dueLabel(node.attrs.due, today);
371
+ const overdue = !!node.attrs.due && !node.attrs.checked && DUE.test(node.attrs.due) && node.attrs.due < today;
372
+ const date = tag(doc, { name: "date", paths: overdue ? ICONS.clock : ICONS.calendar, label: dateName, aria: `Due date: ${dateName}`, filled: !!node.attrs.due, overdue });
373
+ owner.addEventListener("click", () => ownerPicker(view, node.attrs.id, owner));
374
+ date.addEventListener("click", () => datePicker(view, node.attrs.id, date));
375
+ box.append(owner, date);
376
+ return box;
377
+ }
378
+
379
+ export function focusControl(view, name) {
380
+ const $pos = view.state.selection.$head;
381
+ if ($pos.depth !== 1 || $pos.parent.type !== schema.nodes.list_item || $pos.parent.attrs.kind !== "check") return false;
382
+ const id = $pos.parent.attrs.id;
383
+ const widget = [...view.dom.querySelectorAll(".page-check-controls")].find((el) => el.dataset.item === id);
384
+ const button = widget?.querySelector(`[data-check="${name}"]`);
385
+ if (!button) return false;
386
+ button.dataset.fromKeys = "true";
387
+ button.click();
388
+ return true;
389
+ }
390
+
391
+ export function decorations(st) {
392
+ const widgets = [];
393
+ const today = todayKey();
394
+ st.doc.forEach((node, pos) => {
395
+ if (node.type !== schema.nodes.list_item || node.attrs.kind !== "check") return;
396
+ widgets.push(Decoration.widget(pos + node.nodeSize - 1, (view) => {
397
+ const box = controls(view, node);
398
+ box.dataset.item = node.attrs.id;
399
+ return box;
400
+ }, { key: `${node.attrs.id}:${node.attrs.owner}:${node.attrs.due}:${node.attrs.checked}:${today}`, side: -1, stopEvent: (event) => !!event.target.closest?.(".page-check-chip, .page-check-popup"), ignoreSelection: true }));
401
+ });
402
+ return DecorationSet.create(st.doc, widgets);
403
+ }
404
+
405
+ // A document left open overnight redraws its Today, Tomorrow and overdue tags when
406
+ // the day turns, checked once a minute and whenever the tab comes back.
407
+ function watchDay(view) {
408
+ let day = todayKey();
409
+ const check = () => {
410
+ if (view.isDestroyed || todayKey() === day) return;
411
+ day = todayKey();
412
+ view.dispatch(view.state.tr.setMeta("addToHistory", false));
413
+ };
414
+ const win = view.dom.ownerDocument.defaultView;
415
+ const timer = win.setInterval(check, 60000);
416
+ win.document.addEventListener("visibilitychange", check);
417
+ return () => {
418
+ win.clearInterval(timer);
419
+ win.document.removeEventListener("visibilitychange", check);
420
+ };
421
+ }
422
+
423
+ export function createFeature({ view = null } = {}) {
424
+ const off = view ? addViewHook(view, { decorations, focusControl }) : () => {};
425
+ const unwatch = view ? watchDay(view) : () => {};
426
+ if (view && !view.isDestroyed) view.dispatch(view.state.tr.setMeta("addToHistory", false));
427
+ return { id: "checklist", dispose() { off(); unwatch(); } };
428
+ }
@@ -0,0 +1,189 @@
1
+ // The self-contained reading copy, as a command. One snapshot of the live model is
2
+ // taken and rendered through the projection, then each image and attachment the
3
+ // snapshot names is read once and written inside the file. Nothing downloads until
4
+ // the person has seen the captured title, the time it was taken and every resource
5
+ // the copy could not carry: a file that is missing something is never offered, and
6
+ // one that is offered has no address in it that would be fetched again.
7
+ import { project } from "../projection.js";
8
+ import { blockMap } from "../model.js";
9
+ import { PageExportError, renderExportHtml } from "../export-html.js";
10
+ import { inlineExportResources } from "../export-assets.js";
11
+ import { downloadHtml, documentName } from "../../core/download.js";
12
+ import { mountEditorUI } from "../../ui/editor-ui.js";
13
+
14
+ const MAX_NAME = 80;
15
+ const UNSAFE_NAME = /[\u0000-\u001f\u007f<>:"/\\|?*]+/g;
16
+
17
+ // The captured title as a file name: one line, no path of its own, and the page's own
18
+ // name when a title has nothing a file name can keep.
19
+ function fileName(title) {
20
+ const base = String(title ?? "").replace(UNSAFE_NAME, " ").replace(/\s+/g, " ").replace(/^\.+/, "").trim().slice(0, MAX_NAME).trim();
21
+ if (base) return `${base}.html`;
22
+ try { return `${documentName()}.html`; } catch { return "page.html"; }
23
+ }
24
+
25
+ // The panel sits in its own shadow root beside the editor, so the page's CSS cannot restyle
26
+ // it. The page's --page-* tokens still reach it, and it keeps the editor's font and colour.
27
+ const PANEL_CSS = `
28
+ :host{font:inherit !important;color:inherit !important}
29
+ .page-export-panel{max-width:46ch;margin:8px 0;padding:10px 12px;border:1px solid var(--page-pop-line);border-radius:8px;background:var(--page-pop-bg);color:var(--page-pop-ink);box-shadow:var(--page-pop-shadow);font-size:.88em}
30
+ .page-export-title{margin:0 0 2px;font-weight:600}
31
+ .page-export-when{margin:0 0 6px;color:var(--page-ink-soft)}
32
+ .page-export-losses{margin:6px 0;padding-left:1.1em}
33
+ .page-export-losses li{margin:2px 0}
34
+ .page-export-acts{display:flex;gap:6px;margin-top:8px}
35
+ .page-export-btn{border:0;padding:4px 8px;border-radius:6px;background:none;color:inherit;font:inherit;cursor:pointer}
36
+ .page-export-btn:hover{background:var(--page-pop-hover)}
37
+ .page-export-panel[data-state="loss"] .page-export-title{color:var(--page-danger, #9E3B2F)}
38
+ `;
39
+
40
+ export function createFeature({ view = null, download = downloadHtml, fetch: fetchImpl = null, now = () => new Date() } = {}) {
41
+ if (!view) return { run: () => ({ ok: false, code: "not-editing" }), dispose() {} };
42
+ const doc = view.dom.ownerDocument;
43
+ let session = null;
44
+ let disposed = false;
45
+
46
+ const listen = (s, target, type, fn) => {
47
+ target.addEventListener(type, fn);
48
+ s.listen.push([target, type, fn]);
49
+ };
50
+
51
+ const button = (act, label) => {
52
+ const b = doc.createElement("button");
53
+ b.type = "button";
54
+ b.className = "page-export-btn";
55
+ b.dataset.act = act;
56
+ b.textContent = label;
57
+ return b;
58
+ };
59
+
60
+ function close() {
61
+ const s = session;
62
+ if (!s) return;
63
+ session = null;
64
+ for (const [target, type, fn] of s.listen) target.removeEventListener(type, fn);
65
+ s.abort?.abort();
66
+ s.ui?.remove();
67
+ }
68
+
69
+ // One panel for the whole session: what was captured, what could not be carried, and
70
+ // the one action that does anything — a download, and only when nothing is missing.
71
+ function draw(s) {
72
+ const box = s.panel ?? (s.panel = doc.createElement("aside"));
73
+ box.className = "page-export-panel";
74
+ box.dataset.state = s.ready ? "ready" : "loss";
75
+ box.setAttribute("contenteditable", "false");
76
+ box.setAttribute("role", "dialog");
77
+ box.setAttribute("aria-modal", "false");
78
+ box.setAttribute("aria-labelledby", "page-export-title");
79
+ box.dataset.at = s.at.toISOString();
80
+ box.setAttribute("tabindex", "-1");
81
+ box.textContent = "";
82
+ const title = doc.createElement("p");
83
+ title.className = "page-export-title";
84
+ title.id = "page-export-title";
85
+ const heading = s.ready ? "Reading copy" : "This page cannot be a reading copy yet";
86
+ title.textContent = s.title ? `${heading}: ${s.title}` : heading;
87
+ const when = doc.createElement("p");
88
+ when.className = "page-export-when";
89
+ const said = `${s.at.toLocaleString()} · ${s.embedded} ${s.embedded === 1 ? "resource" : "resources"} inside the file`;
90
+ when.textContent = said;
91
+ box.append(title, when);
92
+ if (s.losses.length) {
93
+ const list = doc.createElement("ul");
94
+ list.className = "page-export-losses";
95
+ for (const entry of s.losses) {
96
+ const item = doc.createElement("li");
97
+ item.textContent = entry.message;
98
+ list.append(item);
99
+ }
100
+ box.append(list);
101
+ }
102
+ const acts = doc.createElement("div");
103
+ acts.className = "page-export-acts";
104
+ if (s.ready) acts.append(button("download", "Download"), button("close", "Close"));
105
+ else acts.append(button("retry", "Try again"), button("close", "Close"));
106
+ box.append(acts);
107
+ const first = acts.firstChild;
108
+ if (!s.ui) s.ui = mountEditorUI({ name: "page-export", content: box, css: [PANEL_CSS], parent: view.dom.parentElement ?? doc.body });
109
+ // The panel is what a person lands on: the download when there is one to take,
110
+ // and the list of what stopped it when there is not.
111
+ (s.ready ? first : box).focus?.();
112
+ }
113
+
114
+ function decide(s) {
115
+ listen(s, s.panel, "click", (event) => {
116
+ const b = event.target.closest?.("[data-act]");
117
+ if (!b) return;
118
+ event.preventDefault();
119
+ if (b.dataset.act === "close") close();
120
+ else if (b.dataset.act === "download") save(s);
121
+ else if (b.dataset.act === "retry") run().catch((error) => console.error("clayjs: the reading copy could not be made", error));
122
+ });
123
+ listen(s, doc, "keydown", (event) => {
124
+ if (event.key !== "Escape" || session !== s) return;
125
+ event.preventDefault();
126
+ event.stopPropagation();
127
+ close();
128
+ }, true);
129
+ }
130
+
131
+ // The one place a reading copy becomes a file. The download is handed the bytes that
132
+ // were shown, never a second render, so what a person saw is what they get.
133
+ function save(s) {
134
+ if (!s.ready || disposed) return { ok: false, code: "not-editing" };
135
+ const saved = download({ html: s.ready.html, name: s.ready.name });
136
+ close();
137
+ return { ok: saved === true, downloaded: saved === true };
138
+ }
139
+
140
+ async function run() {
141
+ if (disposed || view.isDestroyed) return { ok: false, code: "not-editing" };
142
+ close();
143
+ const s = { at: now(), title: "", panel: null, listen: [], abort: new AbortController(), losses: [], embedded: 0, ready: null };
144
+ // The model is read once, before anything is awaited: a peer's edit while the
145
+ // resources arrive cannot change the file being made.
146
+ const blocks = blockMap(view.state.doc);
147
+ const model = project(blocks);
148
+ const base = doc.baseURI;
149
+ let rendered;
150
+ try {
151
+ rendered = renderExportHtml(model, {
152
+ fallbackOf: (id) => (typeof blocks[id]?.artifact?.fallbackHtml === "string" ? blocks[id].artifact.fallbackHtml : ""),
153
+ doc,
154
+ });
155
+ } catch (error) {
156
+ if (!(error instanceof PageExportError)) throw error;
157
+ s.title = null;
158
+ s.losses = [{ code: error.code, message: error.message }];
159
+ session = s;
160
+ draw(s);
161
+ decide(s);
162
+ return { ok: false, code: error.code, message: error.message };
163
+ }
164
+ s.title = rendered.title || "Untitled";
165
+ session = s;
166
+ const transport = fetchImpl ?? globalThis.fetch;
167
+ const result = await inlineExportResources(rendered.html, { resources: rendered.resources, base, fetch: transport, signal: s.abort.signal });
168
+ if (disposed || view.isDestroyed || session !== s) return { ok: false, code: "not-editing" };
169
+ s.embedded = result.embedded;
170
+ s.losses = result.losses.slice();
171
+ for (const entry of result.remote ?? []) {
172
+ s.losses.push({ code: "remote-reference", message: `The ${entry.tag} in the reading copy still points at ${String(entry.value).slice(0, 120)}` });
173
+ }
174
+ if (!s.losses.length) s.ready = { html: result.html, name: fileName(s.title) };
175
+ draw(s);
176
+ decide(s);
177
+ return s.ready
178
+ ? { ok: true, title: s.title, name: s.ready.name, embedded: s.embedded }
179
+ : { ok: false, code: "resource-losses", losses: s.losses };
180
+ }
181
+
182
+ return {
183
+ run,
184
+ dispose() {
185
+ disposed = true;
186
+ close();
187
+ },
188
+ };
189
+ }