@panphora/clayjs 1.17.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 (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-F449FF98.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
@@ -1,19 +1,63 @@
1
1
  import { schema } from "../schema.js";
2
2
  import { orderedIds } from "../model.js";
3
- import { DEFAULT_WIDTH, MAX_WIDTH, MIN_WIDTH, addColumn, addRow, deleteColumn, deleteRow, moveColumn, moveRow, renameColumn, selectTableCell, setCellText, setColumnKind, setColumnWidth, sortBy } from "../table.js";
3
+ import { DEFAULT_WIDTH, MAX_WIDTH, MIN_WIDTH, addColumn, addRow, deleteColumn, deleteRow, moveColumn, moveRow, renameColumn, selectTableCell, setCellText, setColumnKind, setColumnWidth, setColumnWidths, clearColumnWidths, sortBy } from "../table.js";
4
4
  import { COLUMN_KINDS } from "../rich/csv.js";
5
5
  import { KIND_ICONS, KIND_LABELS, columnKinds } from "../table-kinds.js";
6
- import { openPageMenu } from "../chrome.js";
6
+ import { openPageMenu, reanchorPageMenu } from "../chrome.js";
7
7
  import { loadChartPart } from "../items.js";
8
8
  import { selectLine } from "../table-select.js";
9
9
  import { TABLE_CSS } from "../table-style.js";
10
10
  import { cellViews } from "../table-views.js";
11
11
  import { asStep } from "../blocksel.js";
12
+ import { announce } from "../announce.js";
13
+ import { registerUnsavedState } from "../../lib/unsaved-state.js";
12
14
 
13
15
  const N = schema.nodes;
14
16
 
15
17
  const columns = (table) => orderedIds(table.attrs.cols);
16
18
 
19
+ const UNAVAILABLE = "That part of the table is no longer available.";
20
+ const GEAR = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><g transform="scale(0.6667)" stroke-width="2.25"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></g></svg>';
21
+ const ACTIONS_CSS = `
22
+ .page-editor .page-table-actions { position: absolute; z-index: 2; bottom: 100%; inset-inline-end: 0; inset-inline-start: auto; width: 24px; height: 20px; padding: 0; display: grid; place-items: center; border: 1px solid var(--page-chrome-line-rest); border-bottom: 0; border-radius: 6px 6px 0 0; background: var(--page-chrome-bg-moved); color: var(--page-chrome-ink-moved); font: inherit; cursor: pointer; opacity: 1; }
23
+ .page-editor .page-table-actions:hover { background: var(--page-pop-hover); color: var(--page-ink-soft); }
24
+ @media (max-width: 560px) {
25
+ .page-editor .page-table-actions { position: absolute; }
26
+ .page-editor .page-table-actions::after { content: ""; position: absolute; inset: -12px; }
27
+ }
28
+ `;
29
+
30
+ const headerId = (tableId, colId) => `${tableId}-${colId}`;
31
+ const columnLabel = (col, index) => col.name || `Column ${index + 1}`;
32
+ const rowLabel = (row, index) => (row.childCount ? row.child(0).textContent.trim() : "") || `Row ${index + 1}`;
33
+
34
+ const tableById = (doc, id) => {
35
+ let found = null;
36
+ doc.forEach((node, offset) => { if (!found && node.type === N.table && node.attrs.id === id) found = { table: node, pos: offset }; });
37
+ return found;
38
+ };
39
+ const rowIndexOf = (table, rowId) => (rowId == null ? 0 : [...table.content.content].findIndex((row) => row.attrs.id === rowId));
40
+ const colIndexOf = (table, colId) => (colId == null ? 0 : columns(table).indexOf(colId));
41
+
42
+ // A table command names its table, row and column when it is offered and finds them
43
+ // again here, so a peer's edit between the two can never hand it another one.
44
+ const selectOn = (view, target, kind) => {
45
+ const found = tableById(view.state.doc, target.tableId);
46
+ if (!found) return false;
47
+ const index = kind === "row" ? rowIndexOf(found.table, target.rowId) : colIndexOf(found.table, target.colId);
48
+ return index < 0 ? false : selectLine(view, found.pos, kind, index);
49
+ };
50
+ const runOn = (view, target, command) => {
51
+ const found = tableById(view.state.doc, target.tableId);
52
+ if (!found) return false;
53
+ const row = rowIndexOf(found.table, target.rowId);
54
+ const col = colIndexOf(found.table, target.colId);
55
+ if (row < 0 || col < 0) return false;
56
+ selectTableCell(view, found.pos, row, col);
57
+ if (command(view.state, null) !== true) return false;
58
+ return command(view.state, view.dispatch);
59
+ };
60
+
17
61
  const FONT = ["fontFamily", "fontSize", "fontWeight", "fontStyle", "letterSpacing"];
18
62
  const textWidth = (el) => {
19
63
  const doc = el.ownerDocument;
@@ -44,7 +88,10 @@ export class TableView {
44
88
  this.view = view;
45
89
  this.getPos = getPos;
46
90
  this.preview = null;
47
- this.focusCol = null;
91
+ this.renaming = null;
92
+ this.renamingUnsaved = null;
93
+ this.menuAnchor = null;
94
+ this.destroyed = false;
48
95
  const doc = view.dom.ownerDocument;
49
96
  this.dom = doc.createElement("div");
50
97
  this.dom.className = "page-table";
@@ -54,6 +101,7 @@ export class TableView {
54
101
  const scroll = doc.createElement("div");
55
102
  scroll.className = "page-table-scroll";
56
103
  const table = doc.createElement("table");
104
+ table.setAttribute("aria-label", "Table");
57
105
  this.table = table;
58
106
  this.colgroup = doc.createElement("colgroup");
59
107
  this.header = doc.createElement("thead");
@@ -69,13 +117,180 @@ export class TableView {
69
117
  addColumnButton.className = "page-table-add-col";
70
118
  addColumnButton.setAttribute("aria-label", "Add a column");
71
119
  addColumnButton.title = "Add a column";
72
- for (const control of [addRowButton, addColumnButton]) {
120
+ const actions = doc.createElement("button");
121
+ actions.type = "button";
122
+ actions.className = "page-table-actions";
123
+ actions.innerHTML = GEAR;
124
+ actions.dataset.control = "actions";
125
+ actions.setAttribute("aria-haspopup", "menu");
126
+ actions.setAttribute("aria-expanded", "false");
127
+ actions.setAttribute("aria-label", "Table actions");
128
+ actions.title = "Table actions";
129
+ this.actionsButton = actions;
130
+ actions.addEventListener("click", (event) => {
131
+ event.preventDefault();
132
+ this.menuAnchor = actions;
133
+ this.openActions();
134
+ });
135
+ actions.addEventListener("keydown", (event) => {
136
+ if (event.key !== "Enter" && event.key !== " ") return;
137
+ event.preventDefault();
138
+ this.menuAnchor = actions;
139
+ this.openActions();
140
+ });
141
+ for (const control of [addRowButton, addColumnButton, actions]) {
73
142
  control.contentEditable = "false";
74
143
  control.addEventListener("mousedown", (event) => event.preventDefault());
75
144
  }
76
- this.dom.append(scroll, addRowButton, addColumnButton);
145
+ this.dom.append(scroll, addRowButton, addColumnButton, actions);
77
146
  this.render();
78
147
  }
148
+ // The control a focus descriptor names, or null when it is gone.
149
+ control(focus) {
150
+ if (!focus || (focus.tableId && focus.tableId !== this.node.attrs.id)) return null;
151
+ const all = [...this.dom.querySelectorAll(`[data-control="${focus.control}"]`)];
152
+ return all.find((el) => (focus.colId == null || el.dataset.col === focus.colId)
153
+ && (focus.rowId == null || el.dataset.row === focus.rowId)) ?? null;
154
+ }
155
+ controls(control) {
156
+ return [...this.dom.querySelectorAll(`[data-control="${control}"]`)].filter((el) => !el.hidden && !el.closest("[hidden]"));
157
+ }
158
+ // Focus stays on the same control; when a peer removed it, the control that took
159
+ // its place in the column or row, and otherwise the table's own exit.
160
+ focusControl(focus) {
161
+ if (!focus || (focus.tableId && focus.tableId !== this.node.attrs.id)) return null;
162
+ const exact = this.control(focus);
163
+ const peers = exact ? [] : this.controls(focus.control);
164
+ const near = peers.length ? peers[Math.min(focus.index ?? 0, peers.length - 1)] : null;
165
+ const target = exact ?? near ?? this.actionsButton;
166
+ target?.focus?.();
167
+ return target;
168
+ }
169
+ describe(element) {
170
+ if (!element || !this.dom.contains(element)) return null;
171
+ const control = element.dataset?.control;
172
+ if (!control) return null;
173
+ return {
174
+ tableId: this.node.attrs.id,
175
+ rowId: element.dataset.row ?? null,
176
+ colId: element.dataset.col ?? null,
177
+ control,
178
+ index: this.controls(control).indexOf(element),
179
+ };
180
+ }
181
+ // Cell views are updated after this one, so a row control a peer removed only
182
+ // finds its replacement once the whole render is done.
183
+ afterUpdate(focus) {
184
+ queueMicrotask(() => {
185
+ if (this.destroyed || this.view.isDestroyed) return;
186
+ const active = this.dom.ownerDocument.activeElement;
187
+ if (active && this.dom.contains(active)) return;
188
+ this.focusControl(focus);
189
+ });
190
+ }
191
+ act(target, command, said) {
192
+ const done = runOn(this.view, target, command);
193
+ announce(this.view, done ? said : UNAVAILABLE);
194
+ return done;
195
+ }
196
+ // What a menu opened now acts on: the caret's cell of this table, by id.
197
+ cellTarget(table = this.node) {
198
+ const pos = this.getPos();
199
+ const { $head } = this.view.state.selection;
200
+ let row = 0, cell = 0;
201
+ for (let depth = $head.depth; depth > 0; depth--) {
202
+ if ($head.node(depth).type !== N.table) continue;
203
+ if (pos != null && $head.before(depth) === pos) {
204
+ row = depth < $head.depth ? $head.index(depth) : 0;
205
+ cell = depth + 1 < $head.depth ? $head.index(depth + 1) : 0;
206
+ }
207
+ break;
208
+ }
209
+ const rows = [...table.content.content];
210
+ return { tableId: table.attrs.id, rowId: rows[row]?.attrs.id ?? null, colId: columns(table)[cell] ?? null };
211
+ }
212
+ openActions() {
213
+ const target = this.cellTarget();
214
+ const table = tableById(this.view.state.doc, target.tableId)?.table;
215
+ if (!table) return false;
216
+ return this.actions(table, target);
217
+ }
218
+ actions(table, target) {
219
+ const ids = columns(table);
220
+ const index = Math.max(0, colIndexOf(table, target.colId));
221
+ const row = Math.max(0, rowIndexOf(table, target.rowId));
222
+ const col = table.attrs.cols[ids[index]] ?? {};
223
+ const sort = table.attrs.sort;
224
+ const at = (command, said) => () => this.act(target, command, said);
225
+ const width = this.widthOfColumn(ids[index]);
226
+ return openPageMenu(this.view, this.actionsButton, [
227
+ { label: "Insert row above", icon: "+", run: at(addRow(false), "Row inserted") },
228
+ { label: "Insert row below", icon: "+", run: at(addRow(true), "Row inserted") },
229
+ ...(row > 0 ? [{ label: "Move row up", icon: "↑", run: at(moveRow(-1), "Row moved") }] : []),
230
+ ...(row < table.childCount - 1 ? [{ label: "Move row down", icon: "↓", run: at(moveRow(1), "Row moved") }] : []),
231
+ { label: "Delete row", icon: "×", run: at(deleteRow, "Row deleted") },
232
+ { label: "Insert column left", icon: "+", run: at(addColumn(false), "Column inserted") },
233
+ { label: "Insert column right", icon: "+", run: at(addColumn(true), "Column inserted") },
234
+ ...(index > 0 ? [{ label: "Move column left", icon: "←", run: at(moveColumn(-1), "Column moved") }] : []),
235
+ ...(index < ids.length - 1 ? [{ label: "Move column right", icon: "→", run: at(moveColumn(1), "Column moved") }] : []),
236
+ ...(ids.length > 1 ? [{ label: "Delete column", icon: "×", run: at(deleteColumn, "Column deleted") }] : []),
237
+ { label: "Sort ascending", icon: "↑", checked: sort?.col === ids[index] && sort.dir === "asc", run: at(sortBy(ids[index], "asc"), "Sorted ascending") },
238
+ { label: "Sort descending", icon: "↓", checked: sort?.col === ids[index] && sort.dir === "desc", run: at(sortBy(ids[index], "desc"), "Sorted descending") },
239
+ { label: "Chart this table", icon: "📊", run: () => { const at = this.getPos(); return loadChartPart(this.view).then(({ insertChartAfterTable }) => insertChartAfterTable(this.view, at)); } },
240
+ { label: "Rename column", focus: false, run: () => this.beginRename(ids[index]) },
241
+ { label: "Column width ▸", icon: "↔", hint: col.width ? `${col.width}px` : "Auto", focus: false, run: () => openPageMenu(this.view, this.actionsButton, [
242
+ { label: "Wider", icon: "+", run: at(this.widthCommand(ids[index], width + 40), "Column width changed") },
243
+ { label: "Narrower", icon: "−", run: at(this.widthCommand(ids[index], width - 40), "Column width changed") },
244
+ { label: "Fit to content", icon: "↔", run: () => this.fit(ids[index]) },
245
+ ...(this.isSized() ? [{ label: "Reset widths", icon: "×", run: at(clearColumnWidths(ids[index]), "Column widths reset") }] : []),
246
+ ]) },
247
+ ], { persist: true });
248
+ }
249
+ // The name field for a column. A peer's edit does not take it away while it has
250
+ // the focus, and a cancelled one writes nothing. A name typed here is unsaved work
251
+ // until Enter or a blur commits it, so the exit asks the registry by its name and
252
+ // refuses while the field holds a name the table does not.
253
+ beginRename(colId) {
254
+ const control = this.control({ control: "column", colId });
255
+ const col = this.node.attrs.cols[colId];
256
+ if (!control || !col) return false;
257
+ const doc = this.dom.ownerDocument;
258
+ const tableId = this.node.attrs.id;
259
+ const input = doc.createElement("input");
260
+ input.type = "text";
261
+ input.className = "page-table-rename";
262
+ input.dataset.control = "rename";
263
+ input.dataset.col = colId;
264
+ input.value = col.name ?? "";
265
+ input.setAttribute("aria-label", "Column name");
266
+ const unsaved = registerUnsavedState({
267
+ id: "table-rename",
268
+ isPending: () => this.renaming === input && input.value.trim() !== (col.name ?? ""),
269
+ });
270
+ this.renamingUnsaved = unsaved;
271
+ const finish = (save) => {
272
+ if (this.renaming !== input) return;
273
+ this.renaming = null;
274
+ this.renamingUnsaved = null;
275
+ unsaved.dispose();
276
+ const value = input.value.trim();
277
+ if (save && value) this.act({ tableId, colId }, renameColumn(colId, value), "Column renamed");
278
+ if (input.isConnected) input.replaceWith(control);
279
+ this.focusControl({ tableId, control: "column", colId });
280
+ };
281
+ input.addEventListener("keydown", (event) => {
282
+ if (event.isComposing || event.keyCode === 229) return;
283
+ if (event.key === "Enter") { event.preventDefault(); finish(true); }
284
+ if (event.key === "Escape") { event.preventDefault(); finish(false); }
285
+ event.stopPropagation();
286
+ });
287
+ input.addEventListener("blur", () => finish(true));
288
+ this.renaming = input;
289
+ control.replaceWith(input);
290
+ input.focus();
291
+ input.select();
292
+ return true;
293
+ }
79
294
  append(what) {
80
295
  const pos = this.getPos();
81
296
  if (pos == null) return;
@@ -85,6 +300,7 @@ export class TableView {
85
300
  this.view.focus();
86
301
  }
87
302
  widths() {
303
+ this.frozen = null;
88
304
  const sized = columns(this.node).some((id) => this.node.attrs.cols[id].width);
89
305
  const cols = columns(this.node).map((id) => {
90
306
  const col = this.dom.ownerDocument.createElement("col");
@@ -101,17 +317,17 @@ export class TableView {
101
317
  const doc = this.dom.ownerDocument;
102
318
  const handle = doc.createElement("span");
103
319
  handle.className = "page-table-resize";
320
+ handle.dataset.control = "resize";
104
321
  handle.dataset.col = colId;
105
322
  handle.setAttribute("role", "separator");
106
323
  handle.setAttribute("aria-orientation", "vertical");
107
- handle.setAttribute("aria-label", `Resize column ${col.name || `Column ${index + 1}`}`);
324
+ handle.setAttribute("aria-label", `Resize column ${columnLabel(col, index)}`);
108
325
  handle.setAttribute("aria-valuemin", String(MIN_WIDTH));
109
326
  handle.setAttribute("aria-valuemax", String(MAX_WIDTH));
110
327
  handle.setAttribute("aria-valuenow", String(this.widthOfColumn(colId)));
111
328
  handle.tabIndex = 0;
112
- handle.title = "Drag or use the arrow keys to resize, double-click to fit";
329
+ handle.title = "Drag or use the arrow keys to resize, double-click to reset the table to the page width";
113
330
  handle.addEventListener("mousedown", (event) => event.preventDefault());
114
- handle.addEventListener("focus", () => { this.focusCol = colId; });
115
331
  handle.addEventListener("blur", () => this.commitWidth(colId));
116
332
  handle.addEventListener("keydown", (event) => this.resizeKey(event, colId));
117
333
  handle.addEventListener("keyup", (event) => {
@@ -120,28 +336,63 @@ export class TableView {
120
336
  handle.addEventListener("pointerdown", (event) => {
121
337
  event.preventDefault();
122
338
  const start = event.clientX;
123
- const from = this.node.attrs.cols[colId].width ?? (th.getBoundingClientRect().width || DEFAULT_WIDTH);
339
+ const from = this.widthOfColumn(colId);
340
+ this.freeze();
124
341
  const rtl = this.dom.ownerDocument.defaultView.getComputedStyle?.(this.dom).direction === "rtl";
125
342
  let width = from;
126
343
  const move = (e) => {
127
344
  width = widthOf(from + (rtl ? start - e.clientX : e.clientX - start)) ?? from;
128
- const col = [...this.colgroup.children].find((el) => el.dataset.col === colId);
129
- if (col) { this.table.classList.add("is-sized"); col.style.width = `${width}px`; }
345
+ this.showWidth(colId, width);
130
346
  };
131
347
  const up = () => {
132
348
  doc.removeEventListener("pointermove", move);
133
349
  doc.removeEventListener("pointerup", up);
134
- if (width !== from) this.command(setColumnWidth(colId, width));
350
+ if (width !== from) this.sizeColumn(colId, width);
135
351
  else this.widths();
136
352
  };
137
353
  doc.addEventListener("pointermove", move);
138
354
  doc.addEventListener("pointerup", up);
139
355
  });
140
- handle.addEventListener("dblclick", (event) => { event.preventDefault(); this.fit(colId); });
356
+ handle.addEventListener("dblclick", (event) => { event.preventDefault(); this.command(colId, clearColumnWidths(colId)); });
141
357
  return handle;
142
358
  }
359
+ isSized() {
360
+ return columns(this.node).some((id) => this.node.attrs.cols[id].width);
361
+ }
362
+ // A table with no widths fills the page, so its columns are measured as drawn.
363
+ drawnWidths() {
364
+ const widths = {};
365
+ for (const th of this.header.querySelectorAll("th")) widths[th.dataset.col] = Math.round(th.getBoundingClientRect().width) || DEFAULT_WIDTH;
366
+ return widths;
367
+ }
143
368
  widthOfColumn(colId) {
144
- return this.node.attrs.cols[colId]?.width ?? DEFAULT_WIDTH;
369
+ const width = this.node.attrs.cols[colId]?.width;
370
+ if (width) return width;
371
+ return this.isSized() ? DEFAULT_WIDTH : this.drawnWidths()[colId] ?? DEFAULT_WIDTH;
372
+ }
373
+ // Before a preview changes one column of a table that fills the page, every
374
+ // column is pinned at its drawn width, so the others stay where they are.
375
+ // Those widths are kept for the gesture: the table's total follows the
376
+ // previewed column, and the commit saves them rather than measuring again.
377
+ freeze() {
378
+ if (this.isSized() || this.frozen) return;
379
+ this.frozen = this.drawnWidths();
380
+ for (const col of this.colgroup.children) col.style.width = `${this.frozen[col.dataset.col]}px`;
381
+ this.table.style.width = `${Object.values(this.frozen).reduce((sum, w) => sum + w, 0)}px`;
382
+ this.table.classList.add("is-sized");
383
+ }
384
+ showWidth(colId, width) {
385
+ const col = [...this.colgroup.children].find((el) => el.dataset.col === colId);
386
+ if (!col) return;
387
+ this.table.classList.add("is-sized");
388
+ col.style.width = `${width}px`;
389
+ if (this.frozen) this.table.style.width = `${Object.values({ ...this.frozen, [colId]: width }).reduce((sum, w) => sum + w, 0)}px`;
390
+ }
391
+ widthCommand(colId, width) {
392
+ return this.isSized() ? setColumnWidth(colId, width) : setColumnWidths({ ...(this.frozen ?? this.drawnWidths()), [colId]: width }, colId);
393
+ }
394
+ sizeColumn(colId, width) {
395
+ return this.command(colId, this.widthCommand(colId, width));
145
396
  }
146
397
  handleFor(colId) {
147
398
  return [...this.header.querySelectorAll(".page-table-resize")].find((handle) => handle.dataset.col === colId) ?? null;
@@ -164,9 +415,9 @@ export class TableView {
164
415
  }
165
416
  previewWidth(colId, width) {
166
417
  const base = this.preview?.col === colId ? this.preview.base : this.widthOfColumn(colId);
418
+ this.freeze();
167
419
  this.preview = { col: colId, width, base };
168
- const col = [...this.colgroup.children].find((element) => element.dataset.col === colId);
169
- if (col) { this.table.classList.add("is-sized"); col.style.width = `${width}px`; }
420
+ this.showWidth(colId, width);
170
421
  const handle = this.handleFor(colId);
171
422
  if (handle) handle.setAttribute("aria-valuenow", String(width));
172
423
  }
@@ -185,106 +436,120 @@ export class TableView {
185
436
  const table = pos == null ? null : this.view.state.doc.nodeAt(pos);
186
437
  const col = table?.type === N.table && table.attrs.id === this.node.attrs.id ? table.attrs.cols[colId] : null;
187
438
  if (!col) { this.widths(); return false; }
188
- if ((col.width ?? DEFAULT_WIDTH) === preview.width) { this.widths(); return false; }
189
- const cols = { ...table.attrs.cols, [colId]: { ...col, width: preview.width } };
439
+ if ((col.width ?? preview.base) === preview.width) { this.widths(); return false; }
440
+ const seed = columns(table).some((id) => table.attrs.cols[id].width) ? {} : this.frozen ?? this.drawnWidths();
441
+ const cols = { ...table.attrs.cols };
442
+ for (const [id, width] of Object.entries(seed)) if (cols[id]) cols[id] = { ...cols[id], width };
443
+ cols[colId] = { ...col, width: preview.width };
190
444
  this.view.dispatch(asStep(this.view.state.tr.setNodeMarkup(pos, null, { ...table.attrs, cols })));
191
445
  return true;
192
446
  }
193
- command(run) {
194
- const pos = this.getPos();
195
- if (pos == null) return false;
196
- selectTableCell(this.view, pos, 0, 0);
197
- const done = run(this.view.state, this.view.dispatch);
447
+ command(colId, command) {
448
+ const done = runOn(this.view, { tableId: this.node.attrs.id, colId }, command);
198
449
  this.view.focus();
199
450
  return done;
200
451
  }
201
452
  fit(colId) {
202
453
  let widest = 0;
203
- for (const cell of this.contentDOM.querySelectorAll("td")) if (cell.dataset.col === colId) widest = Math.max(widest, textWidth(cell.firstElementChild ?? cell));
454
+ for (const cell of this.contentDOM.querySelectorAll("td")) if (cell.dataset.col === colId) widest = Math.max(widest, textWidth(cell.querySelector("span") ?? cell));
204
455
  const head = [...this.header.querySelectorAll("th")].find((th) => th.dataset.col === colId)?.querySelector("button");
205
456
  widest = Math.max(widest, head?.scrollWidth ?? 0);
206
- return this.command(setColumnWidth(colId, widest + 24));
457
+ return this.sizeColumn(colId, widest + 24);
207
458
  }
208
- render(refocusCol = null) {
459
+ render(focus = null) {
209
460
  const doc = this.dom.ownerDocument;
210
461
  const row = doc.createElement("tr");
211
462
  const effective = columnKinds(this.node);
463
+ const tableId = this.node.attrs.id;
212
464
  this.widths();
213
465
  for (const [index, colId] of columns(this.node).entries()) {
214
466
  const col = this.node.attrs.cols[colId];
215
467
  const kind = effective[colId];
468
+ const name = columnLabel(col, index);
216
469
  const th = doc.createElement("th");
217
470
  th.dataset.col = colId;
218
471
  th.dataset.kind = kind;
472
+ th.setAttribute("scope", "col");
473
+ th.id = headerId(tableId, colId);
219
474
  if (this.node.attrs.sort?.col === colId) th.setAttribute("aria-sort", this.node.attrs.sort.dir === "asc" ? "ascending" : "descending");
220
- const control = button(doc, col.name || `Column ${index + 1}`, (event) => {
475
+ const control = doc.createElement("button");
476
+ control.type = "button";
477
+ control.className = "page-table-col-menu";
478
+ control.textContent = name;
479
+ control.dataset.control = "column";
480
+ control.dataset.col = colId;
481
+ control.dataset.kind = kind;
482
+ control.setAttribute("aria-haspopup", "menu");
483
+ control.setAttribute("aria-expanded", "false");
484
+ control.setAttribute("aria-label", `${name} column actions`);
485
+ control.addEventListener("click", (event) => {
221
486
  event.preventDefault();
222
- const run = (command) => () => { selectTableCell(this.view, this.getPos(), 0, index); command(this.view.state, this.view.dispatch); this.view.focus(); };
223
- const rename = () => {
224
- const input = doc.createElement("input");
225
- input.type = "text";
226
- input.value = col.name;
227
- input.setAttribute("aria-label", "Column name");
228
- const finish = (save) => {
229
- if (save && input.value.trim()) run(renameColumn(colId, input.value))();
230
- else if (input.isConnected) { input.replaceWith(control); control.focus(); }
231
- };
232
- input.addEventListener("keydown", (e) => {
233
- if (e.key === "Enter") { e.preventDefault(); finish(true); }
234
- if (e.key === "Escape") { e.preventDefault(); finish(false); }
235
- e.stopPropagation();
236
- });
237
- control.replaceWith(input);
238
- input.focus();
239
- input.select();
240
- };
487
+ this.menuAnchor = control;
488
+ const target = { tableId, colId };
489
+ const run = (command, said) => () => this.act(target, command, said);
241
490
  openPageMenu(this.view, control, [
242
- { label: "Rename", run: rename, focus: false },
491
+ { label: "Rename", run: () => this.beginRename(colId), focus: false },
243
492
  { label: "Kind ▸", icon: KIND_ICONS[kind], hint: col.kind ? KIND_LABELS[kind] : `Auto: ${KIND_LABELS[kind]}`, run: () => openPageMenu(this.view, control, [
244
- { label: "Auto", icon: col.kind ? "" : KIND_ICONS[kind], hint: col.kind ? "" : KIND_LABELS[kind], checked: !col.kind, run: run(setColumnKind(colId, "auto")) },
245
- ...COLUMN_KINDS.map((option) => ({ label: KIND_LABELS[option], icon: KIND_ICONS[option], checked: col.kind === option, run: run(setColumnKind(colId, option)) })),
493
+ { label: "Auto", icon: col.kind ? "" : KIND_ICONS[kind], hint: col.kind ? "" : KIND_LABELS[kind], checked: !col.kind, run: run(setColumnKind(colId, "auto"), `Kind ${KIND_LABELS[kind]}`) },
494
+ ...COLUMN_KINDS.map((option) => ({ label: KIND_LABELS[option], icon: KIND_ICONS[option], checked: col.kind === option, run: run(setColumnKind(colId, option), `Kind ${KIND_LABELS[option]}`) })),
246
495
  ]), focus: false },
247
- { label: "Sort ascending", icon: "↑", run: run(sortBy(colId, "asc")) },
248
- { label: "Sort descending", icon: "↓", run: run(sortBy(colId, "desc")) },
496
+ { label: "Sort ascending", icon: "↑", run: run(sortBy(colId, "asc"), "Sorted ascending") },
497
+ { label: "Sort descending", icon: "↓", run: run(sortBy(colId, "desc"), "Sorted descending") },
249
498
  { label: "Width ▸", icon: "↔", hint: col.width ? `${col.width}px` : "Auto", focus: false, run: () => openPageMenu(this.view, control, [
250
- { label: "Wider", icon: "+", run: () => this.command(setColumnWidth(colId, (col.width ?? (th.getBoundingClientRect().width || DEFAULT_WIDTH)) + 40)) },
251
- { label: "Narrower", icon: "−", run: () => this.command(setColumnWidth(colId, (col.width ?? (th.getBoundingClientRect().width || DEFAULT_WIDTH)) - 40)) },
499
+ { label: "Wider", icon: "+", run: run(this.widthCommand(colId, this.widthOfColumn(colId) + 40), "Column width changed") },
500
+ { label: "Narrower", icon: "−", run: run(this.widthCommand(colId, this.widthOfColumn(colId) - 40), "Column width changed") },
252
501
  { label: "Fit to content", icon: "↔", run: () => this.fit(colId) },
253
- ...(col.width ? [{ label: "Reset width", icon: "×", run: () => this.command(setColumnWidth(colId, null)) }] : []),
502
+ ...(this.isSized() ? [{ label: "Reset widths", icon: "×", run: run(clearColumnWidths(colId), "Column widths reset") }] : []),
254
503
  ]) },
255
504
  { label: "Chart this table", icon: "📊", run: () => { const at = this.getPos(); return loadChartPart(this.view).then(({ insertChartAfterTable }) => insertChartAfterTable(this.view, at)); } },
256
- { label: "Insert column left", icon: "+", run: run(addColumn(false)) },
257
- { label: "Insert column right", icon: "+", run: run(addColumn(true)) },
258
- ...(index > 0 ? [{ label: "Move left", icon: "←", run: run(moveColumn(-1)) }] : []),
259
- ...(index < columns(this.node).length - 1 ? [{ label: "Move right", icon: "→", run: run(moveColumn(1)) }] : []),
260
- { label: "Select column", icon: "▦", run: () => selectLine(this.view, this.getPos(), "col", index) },
261
- { label: "Delete column", icon: "×", run: run(deleteColumn) },
262
- ]);
505
+ { label: "Insert column left", icon: "+", run: run(addColumn(false), "Column inserted") },
506
+ { label: "Insert column right", icon: "+", run: run(addColumn(true), "Column inserted") },
507
+ ...(index > 0 ? [{ label: "Move left", icon: "←", run: run(moveColumn(-1), "Column moved") }] : []),
508
+ ...(index < columns(this.node).length - 1 ? [{ label: "Move right", icon: "→", run: run(moveColumn(1), "Column moved") }] : []),
509
+ { label: "Select column", icon: "▦", run: () => selectOn(this.view, target, "col") },
510
+ { label: "Delete column", icon: "×", run: run(deleteColumn, "Column deleted") },
511
+ ], { persist: true });
263
512
  });
264
- control.dataset.kind = kind;
265
513
  th.append(control, this.resizer(colId, th, col, index));
266
514
  row.append(th);
267
515
  }
516
+ const opened = this.menuAnchor;
268
517
  this.header.replaceChildren(row);
269
- if (refocusCol) this.handleFor(refocusCol)?.focus();
518
+ if (opened) {
519
+ const next = this.control({ tableId, control: opened.dataset.control, colId: opened.dataset.col ?? null, rowId: opened.dataset.row ?? null });
520
+ this.menuAnchor = next && next !== opened && reanchorPageMenu(this.view, opened, next) ? next : null;
521
+ }
522
+ if (focus) this.focusControl(focus);
270
523
  }
271
524
  update(node) {
272
525
  if (node.type !== N.table) return false;
273
- const focused = this.focusCol && this.dom.contains(this.dom.ownerDocument.activeElement);
526
+ const focused = this.describe(this.dom.ownerDocument.activeElement);
274
527
  const preview = this.preview;
528
+ const renaming = this.renaming?.isConnected && focused?.control === "rename";
275
529
  this.node = node;
276
530
  this.dom.dataset.id = node.attrs.id ?? "";
277
531
  this.preview = null;
278
- this.render(focused ? this.focusCol : null);
279
- if (preview && node.attrs.cols[preview.col] && (node.attrs.cols[preview.col].width ?? DEFAULT_WIDTH) === preview.base) {
532
+ // A peer's edit does not take the name being typed out of the field.
533
+ if (renaming) this.widths();
534
+ else this.render(focused);
535
+ if (preview && node.attrs.cols[preview.col] && (node.attrs.cols[preview.col].width ?? preview.base) === preview.base) {
280
536
  this.previewWidth(preview.col, preview.width);
281
537
  }
282
538
  const effective = columnKinds(node);
283
539
  for (const cell of this.contentDOM.querySelectorAll("td[data-col]")) cell.dataset.kind = effective[cell.dataset.col] ?? "text";
540
+ if (focused && !renaming) this.afterUpdate(focused);
284
541
  return true;
285
542
  }
286
- stopEvent(event) { return !!event.target.closest?.("thead"); }
543
+ stopEvent(event) { return !!event.target.closest?.("thead, .page-table-actions, .page-table-add-row, .page-table-add-col"); }
287
544
  ignoreMutation(mutation) { return !this.contentDOM.contains(mutation.target); }
545
+ destroy() {
546
+ this.destroyed = true;
547
+ this.renaming = null;
548
+ this.renamingUnsaved?.dispose();
549
+ this.renamingUnsaved = null;
550
+ this.menuAnchor = null;
551
+ this.preview = null;
552
+ }
288
553
  }
289
554
 
290
555
 
@@ -299,38 +564,63 @@ export class TableCellView {
299
564
  cellViews.set(this.dom, this);
300
565
  this.dom.dataset.col = node.attrs.col;
301
566
  this.contentDOM = doc.createElement("span");
302
- this.dom.append(this.contentDOM);
303
567
  const menu = doc.createElement("button");
304
568
  menu.type = "button";
305
569
  menu.className = "page-table-row-menu";
306
- menu.textContent = "Row actions";
570
+ menu.innerHTML = GEAR;
571
+ menu.dataset.control = "row";
572
+ menu.setAttribute("aria-haspopup", "menu");
573
+ menu.setAttribute("aria-expanded", "false");
307
574
  menu.setAttribute("aria-label", "Row actions");
575
+ menu.title = "Row actions";
308
576
  menu.contentEditable = "false";
309
577
  menu.addEventListener("click", () => {
310
- const $cell = this.view.state.doc.resolve(this.getPos());
311
- const tablePos = $cell.before(1);
312
- const row = $cell.index(1);
313
- const run = (command) => () => { selectTableCell(this.view, tablePos, row, 0); command(this.view.state, this.view.dispatch); this.view.focus(); };
314
- const count = $cell.node(1).childCount;
578
+ const target = this.target();
579
+ if (!target) return;
580
+ const found = tableById(this.view.state.doc, target.tableId);
581
+ if (!found) return;
582
+ const at = (command, said) => () => {
583
+ const done = runOn(this.view, target, command);
584
+ announce(this.view, done ? said : UNAVAILABLE);
585
+ return done;
586
+ };
587
+ const row = Math.max(0, rowIndexOf(found.table, target.rowId));
588
+ const count = found.table.childCount;
315
589
  openPageMenu(this.view, menu, [
316
- { label: "Insert above", icon: "+", run: run(addRow(false)) },
317
- { label: "Insert below", icon: "+", run: run(addRow(true)) },
318
- ...(row > 0 ? [{ label: "Move up", icon: "↑", run: run(moveRow(-1)) }] : []),
319
- ...(row < count - 1 ? [{ label: "Move down", icon: "↓", run: run(moveRow(1)) }] : []),
320
- { label: "Select row", icon: "▦", run: () => selectLine(this.view, tablePos, "row", row) },
321
- { label: "Delete row", icon: "×", run: run(deleteRow) },
322
- ]);
590
+ { label: "Insert above", icon: "+", run: at(addRow(false), "Row inserted") },
591
+ { label: "Insert below", icon: "+", run: at(addRow(true), "Row inserted") },
592
+ ...(row > 0 ? [{ label: "Move up", icon: "↑", run: at(moveRow(-1), "Row moved") }] : []),
593
+ ...(row < count - 1 ? [{ label: "Move down", icon: "↓", run: at(moveRow(1), "Row moved") }] : []),
594
+ { label: "Select row", icon: "▦", run: () => selectOn(this.view, target, "row") },
595
+ { label: "Delete row", icon: "×", run: at(deleteRow, "Row deleted") },
596
+ ], { persist: true });
323
597
  });
324
598
  this.menu = menu;
325
- this.dom.append(menu);
599
+ this.dom.append(menu, this.contentDOM);
326
600
  this.update(node);
327
601
  }
602
+ // The row and column this cell names when its menu opens; both are looked up
603
+ // again by id when a row of the menu runs.
604
+ target() {
605
+ const pos = this.getPos();
606
+ const $cell = pos == null ? null : this.view.state.doc.resolve(pos);
607
+ const table = $cell?.node(1);
608
+ if (!table || table.type !== N.table) return null;
609
+ return { tableId: table.attrs.id, rowId: $cell.node(2).attrs.id ?? null, colId: this.node.attrs.col ?? null };
610
+ }
328
611
  update(node) {
329
612
  if (node.type !== N.table_cell) return false;
330
613
  this.node = node;
331
614
  const $cell = this.view.state.doc.resolve(this.getPos());
332
615
  const table = $cell.node(1);
616
+ const row = $cell.node(2);
617
+ const index = $cell.index(1);
618
+ this.dom.dataset.col = node.attrs.col;
619
+ this.dom.setAttribute("headers", headerId(table.attrs.id, node.attrs.col));
333
620
  this.dom.dataset.kind = columnKinds(table)[node.attrs.col] ?? "text";
621
+ this.menu.dataset.col = node.attrs.col;
622
+ this.menu.dataset.row = row.attrs.id ?? "";
623
+ this.menu.setAttribute("aria-label", `Row actions: ${rowLabel(row, index)}`);
334
624
  this.menu.hidden = $cell.index(2) !== 0;
335
625
  if (this.dom.dataset.kind !== "date") this.closeDate();
336
626
  return true;
@@ -377,7 +667,7 @@ export function createFeature() {
377
667
  table: (node, view, getPos) => new TableView(node, view, getPos),
378
668
  table_cell: (node, view, getPos) => new TableCellView(node, view, getPos),
379
669
  },
380
- css: TABLE_CSS,
670
+ css: TABLE_CSS + ACTIONS_CSS,
381
671
  dispose() {},
382
672
  };
383
673
  }