@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
@@ -3,288 +3,117 @@
3
3
  // the saved file, a sync frame, the dirty check or undo. Nothing is written until
4
4
  // Apply: opening and cancelling change only the sheet.
5
5
  import { applyHead, FIELDS, readHead } from "./seo-head.js";
6
+ import { lookAt } from "../../ui/page-colour.js";
7
+ import MENU_CSS, { MENU_SURFACE } from "./style.js";
8
+ import { mountEditorUI } from "../../ui/editor-ui.js";
9
+ import { deepActiveElement } from "../../dom/active-element.js";
6
10
  import { hostMeta } from "../../core/host-meta.js";
7
11
  import { documentTitle } from "../../page/document-title.js";
8
12
  import { registerUnsavedState } from "../../lib/unsaved-state.js";
9
13
  import { modeRefusalText } from "../../core/mode-refusal.js";
14
+ import { dismissPersistent, toastPersistent } from "../../ui/toast-lazy.js";
10
15
 
11
- const STYLE_ID = "clay-seo-style";
12
16
  const REOPEN_KEY = "reopen-seo";
13
- const LABEL = "SEO settings";
17
+ const LABEL = "SEO and social sharing";
18
+ const UNDO_LABEL = "SEO settings";
19
+ const APPLIED_TEXT = "SEO settings applied";
14
20
  const SCHEME = /^[a-z][a-z0-9+.-]*:/i;
15
21
  const ROBOTS_NOTE =
16
22
  "This site is hidden from search engines in your account settings, so this page stays hidden whatever you choose here.";
17
23
  const PUBLIC_NOTE = "Uploaded images are public to anyone with the link.";
18
24
  const PASTE_NOTE = "Paste the address of an image on the web.";
19
25
 
20
- const GROUPS = [
21
- {
22
- legend: "Basics",
23
- fields: [
24
- { key: "title", label: "Title", kind: "text" },
25
- { key: "description", label: "Description", kind: "textarea" },
26
- { key: "lang", label: "Language", kind: "text" },
27
- { key: "canonical", label: "Canonical URL", kind: "text" },
28
- ],
29
- },
30
- {
31
- legend: "Search",
32
- fields: [
33
- { key: "index", label: "Show in search results", kind: "checkbox" },
34
- { key: "follow", label: "Let search engines follow links", kind: "checkbox" },
35
- ],
36
- },
37
- {
38
- legend: "Link preview",
39
- fields: [
40
- { key: "ogTitle", label: "Preview title", kind: "text" },
41
- { key: "ogDescription", label: "Preview description", kind: "textarea" },
42
- { key: "ogImage", label: "Preview image", kind: "text" },
43
- { key: "ogImageAlt", label: "Image alt text", kind: "text" },
44
- { key: "siteName", label: "Site name", kind: "text" },
45
- ],
46
- },
47
- {
48
- legend: "Social card",
49
- fields: [{ key: "twitterSite", label: "Handle", kind: "text" }],
50
- },
51
- ];
52
-
53
- const SHEET_CSS = `.clay-seo {
54
- position: fixed;
55
- top: 0;
56
- right: 0;
57
- left: 0;
58
- z-index: 2147482100;
59
- box-sizing: border-box;
60
- width: 100%;
61
- max-height: 100vh;
62
- margin: 0;
63
- padding: 16px;
64
- overflow-y: auto;
65
- border-bottom: 1px solid rgba(23, 28, 36, .16);
66
- font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
67
- font-size: 14px;
68
- line-height: 1.4;
69
- color: var(--clay-ui-fg, #1F2328);
70
- background: var(--clay-ui-bg, #F4F4F5);
71
- box-shadow: 0 18px 40px -24px rgba(23, 28, 36, .55);
72
- animation: clay-seo-slide 200ms ease-out;
73
- }
74
- @keyframes clay-seo-slide {
75
- from { transform: translateY(-100%); }
76
- to { transform: translateY(0); }
77
- }
78
- .clay-seo-top {
79
- display: flex;
80
- align-items: center;
81
- justify-content: space-between;
82
- gap: 12px;
83
- }
84
- .clay-seo-heading {
85
- margin: 0;
86
- font-size: 15px;
87
- font-weight: 600;
88
- line-height: 1.2;
89
- }
90
- .clay-seo-x {
91
- width: 32px;
92
- height: 32px;
93
- margin: 0;
94
- padding: 0;
95
- border: 0;
96
- border-radius: 50%;
97
- font: inherit;
98
- font-size: 18px;
99
- line-height: 1;
100
- color: inherit;
101
- background: none;
102
- cursor: pointer;
103
- }
104
- .clay-seo-x:hover,
105
- .clay-seo-x:focus-visible { background: rgba(23, 28, 36, .08); outline: none; }
106
- .clay-seo-preview {
107
- display: flex;
108
- gap: 12px;
109
- margin: 12px 0;
110
- padding: 10px;
111
- border: 1px solid rgba(23, 28, 36, .16);
112
- border-radius: 8px;
113
- }
114
- .clay-seo-preview-image {
115
- flex: none;
116
- width: 88px;
117
- height: 62px;
118
- border-radius: 6px;
119
- object-fit: cover;
120
- }
121
- .clay-seo-preview-text { min-width: 0; }
122
- .clay-seo-preview-title {
123
- margin: 0;
124
- font-weight: 600;
125
- overflow: hidden;
126
- text-overflow: ellipsis;
127
- white-space: nowrap;
128
- }
129
- .clay-seo-preview-description,
130
- .clay-seo-preview-host {
131
- margin: 2px 0 0;
132
- font-size: 12px;
133
- opacity: .8;
134
- }
135
- .clay-seo-group {
136
- margin: 0 0 14px;
137
- padding: 0;
138
- border: 0;
139
- }
140
- .clay-seo-legend {
141
- padding: 0;
142
- margin-bottom: 6px;
143
- font-size: 12px;
144
- font-weight: 600;
145
- letter-spacing: .04em;
146
- text-transform: uppercase;
147
- opacity: .7;
148
- }
149
- .clay-seo-row {
150
- display: block;
151
- margin-bottom: 8px;
152
- }
153
- .clay-seo-caption {
154
- display: block;
155
- margin-bottom: 4px;
156
- font-size: 12px;
157
- opacity: .8;
158
- }
159
- .clay-seo-row-check {
160
- display: flex;
161
- align-items: center;
162
- gap: 8px;
163
- }
164
- .clay-seo-row-check .clay-seo-caption { margin-bottom: 0; }
165
- .clay-seo-input {
166
- box-sizing: border-box;
167
- width: 100%;
168
- margin: 0;
169
- padding: 7px 9px;
170
- border: 1px solid rgba(23, 28, 36, .24);
171
- border-radius: 6px;
172
- font: inherit;
173
- color: inherit;
174
- background: rgba(250, 251, 252, .7);
175
- }
176
- .clay-seo-input:focus-visible { outline: 2px solid var(--clay-ui-accent, #2F6FEB); outline-offset: 1px; }
177
- textarea.clay-seo-input { min-height: 64px; resize: vertical; }
178
- .clay-seo-note {
179
- margin: 4px 0 0;
180
- font-size: 12px;
181
- opacity: .8;
182
- }
183
- .clay-seo-error {
184
- margin: 4px 0 0;
185
- font-size: 12px;
186
- color: #A63A2B;
187
- }
188
- .clay-seo-derived {
189
- margin: 4px 0 0;
190
- padding: 0;
191
- border: 0;
192
- font: inherit;
193
- font-size: 12px;
194
- color: var(--clay-ui-accent, #2F6FEB);
195
- background: none;
196
- text-decoration: underline;
197
- cursor: pointer;
198
- }
199
- .clay-seo-conflict {
200
- margin: 6px 0 0;
201
- padding: 8px;
202
- border: 1px solid rgba(23, 28, 36, .16);
203
- border-radius: 6px;
204
- font-size: 12px;
205
- }
206
- .clay-seo-conflict p { margin: 0 0 6px; }
207
- .clay-seo-upload {
208
- display: flex;
209
- flex-wrap: wrap;
210
- align-items: center;
211
- gap: 8px;
212
- margin: 4px 0 8px;
213
- }
214
- .clay-seo-upload .clay-seo-note { flex: 1 1 100%; margin: 0; }
215
- .clay-seo-actions {
216
- display: flex;
217
- justify-content: flex-end;
218
- gap: 8px;
219
- margin-top: 12px;
220
- }
221
- .clay-seo-actions button {
222
- padding: 8px 14px;
223
- border: 1px solid rgba(23, 28, 36, .24);
224
- border-radius: 6px;
225
- font: inherit;
226
- color: inherit;
227
- background: none;
228
- cursor: pointer;
229
- }
230
- .clay-seo-actions button:hover,
231
- .clay-seo-actions button:focus-visible { outline: none; background: rgba(23, 28, 36, .08); }
232
- .clay-seo-apply,
233
- .clay-seo-edit {
234
- border-color: transparent;
235
- color: #F7F8FA;
236
- background: var(--clay-ui-accent, #2F6FEB);
237
- }
238
- .clay-seo-actions .clay-seo-apply:hover,
239
- .clay-seo-actions .clay-seo-apply:focus-visible,
240
- .clay-seo-actions .clay-seo-edit:hover,
241
- .clay-seo-actions .clay-seo-edit:focus-visible { background: var(--clay-ui-accent, #2F6FEB); opacity: .9; }
242
- .clay-seo-upload-button {
243
- padding: 6px 10px;
244
- border: 1px solid rgba(23, 28, 36, .24);
245
- border-radius: 6px;
246
- font: inherit;
247
- font-size: 12px;
248
- color: inherit;
249
- background: none;
250
- cursor: pointer;
251
- }
252
- @media (min-width: 600px) {
253
- .clay-seo {
254
- width: 560px;
255
- margin: 0 auto;
256
- padding: 16px;
257
- border: 1px solid rgba(23, 28, 36, .16);
258
- border-top: 0;
259
- border-radius: 0 0 12px 12px;
260
- }
261
- }
262
- @media (pointer: coarse) {
263
- .clay-seo-actions button,
264
- .clay-seo-upload-button { min-height: 44px; }
265
- }
266
- @media (prefers-color-scheme: dark) {
267
- .clay-seo {
268
- color: var(--clay-ui-fg, #E9E9EC);
269
- background: var(--clay-ui-bg, #26282C);
270
- border-color: rgba(233, 233, 236, .16);
271
- }
272
- .clay-seo-preview { border-color: rgba(233, 233, 236, .16); }
273
- .clay-seo-input {
274
- border-color: rgba(233, 233, 236, .16);
275
- background: rgba(233, 233, 236, .08);
276
- }
277
- .clay-seo-x:hover,
278
- .clay-seo-x:focus-visible,
279
- .clay-seo-actions button:hover,
280
- .clay-seo-actions button:focus-visible { background: rgba(233, 233, 236, .12); }
281
- .clay-seo-actions button { border-color: rgba(233, 233, 236, .24); }
282
- .clay-seo-conflict { border-color: rgba(233, 233, 236, .16); }
283
- .clay-seo-error { color: #F0A192; }
284
- }
285
- @media (prefers-reduced-motion: reduce) {
286
- .clay-seo { animation: none; }
287
- }`;
26
+ const FIELD = {
27
+ title: { label: "Title", kind: "text", full: true, count: 60 },
28
+ description: { label: "Description", kind: "textarea", full: true, count: 160 },
29
+ ogTitle: { label: "Preview title", note: "Open Graph", kind: "text" },
30
+ ogDescription: { label: "Preview description", note: "Open Graph", kind: "textarea", short: true },
31
+ ogImage: { label: "Preview image", note: "Open Graph", kind: "text", full: true },
32
+ canonical: { label: "Canonical URL", kind: "text" },
33
+ lang: { label: "Language", kind: "text" },
34
+ ogImageAlt: { label: "Image alt text", kind: "text" },
35
+ siteName: { label: "Site name", kind: "text" },
36
+ twitterSite: { label: "Social handle", kind: "text", placeholder: "@yourname" },
37
+ index: { label: "Show in search results", kind: "checkbox" },
38
+ follow: { label: "Let search engines follow links", kind: "checkbox" },
39
+ };
40
+
41
+ const BASE_KEYS = ["title", "description", "ogTitle", "ogDescription", "ogImage"];
42
+ const MORE_KEYS = ["canonical", "lang", "ogImageAlt", "siteName", "twitterSite"];
43
+ const CHECK_KEYS = ["index", "follow"];
44
+
45
+ const SHEET_CSS = `.clay-seo { box-sizing: border-box; width: 100%; border-bottom: 1px solid var(--bevel-line-3); background: var(--bevel-surface); box-shadow: var(--clay-sheet-shadow); font-size: 13px; line-height: 1.45; container-type: inline-size; animation: clay-seo-in .2s ease-out; position: relative; z-index: 2147482001; grid-column: 1 / -1; flex: 0 0 100%; max-height: 85vh; max-height: 85dvh; overflow-y: auto; overscroll-behavior: contain; }
46
+ @keyframes clay-seo-in { from { margin-top: -40px; opacity: 0; } to { margin-top: 0; opacity: 1; } }
47
+ .clay-seo-inner { max-width: 1120px; margin: 0 auto; padding: 14px 20px 18px; }
48
+ .clay-seo-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-bottom: 10px; margin-bottom: 12px; border-bottom: 1px solid var(--bevel-line-2); }
49
+ .clay-seo-heading { margin: 0; font: 500 19px/1.15 var(--bevel-font-serif); letter-spacing: -.01em; }
50
+ .clay-seo-actions { display: flex; gap: 8px; }
51
+ .clay-seo-grid { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(260px, .9fr); gap: 22px; align-items: start; }
52
+ .clay-seo-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; }
53
+ .clay-seo-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
54
+ .clay-seo-field.is-full { grid-column: 1 / -1; }
55
+ .clay-seo-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; font-size: 12.5px; font-weight: 500; color: var(--bevel-ink-2); }
56
+ .clay-seo-label-row label span { font-weight: 400; color: var(--bevel-muted); }
57
+ .clay-seo-field:has([aria-invalid="true"]) .clay-seo-label-row { color: var(--bevel-ox); }
58
+ .clay-seo-aside { display: inline-flex; align-items: baseline; gap: 10px; }
59
+ .clay-seo-count { font: 500 11px/1 var(--bevel-font-mono); color: var(--bevel-muted); font-variant-numeric: tabular-nums; }
60
+ .clay-seo-count[data-over] { color: var(--bevel-ox); }
61
+ .clay-seo-derived { padding: 0; border: 0; background: none; color: var(--bevel-brass); font: 500 12px/1.3 var(--bevel-font-sans); text-decoration: underline; text-decoration-color: var(--bevel-line-3); text-underline-offset: 2px; cursor: pointer; }
62
+ .clay-seo-input { box-sizing: border-box; width: 100%; min-height: 32px; padding: 6px 9px; border: 1px solid var(--bevel-line-2); background: var(--bevel-ground); color: var(--bevel-ink); font: 400 13px/1.45 var(--bevel-font-sans); }
63
+ textarea.clay-seo-input { min-height: 52px; resize: vertical; }
64
+ textarea.clay-seo-input.is-short { min-height: 32px; }
65
+ .clay-seo-input::placeholder { color: var(--bevel-faint); opacity: 1; }
66
+ .clay-seo-input:hover { border-color: var(--bevel-faint); }
67
+ .clay-seo-input:focus-visible { outline: 2px solid var(--bevel-brass); outline-offset: 1px; border-color: var(--bevel-brass); }
68
+ .clay-seo-input[aria-invalid="true"] { border-color: var(--bevel-ox); background: color-mix(in srgb, var(--bevel-ox-soft) 40%, var(--bevel-ground)); }
69
+ .clay-seo-error { margin: 0; font-size: 12px; font-weight: 500; color: var(--bevel-ox); }
70
+ .clay-seo-upload { display: flex; gap: 8px; }
71
+ .clay-seo-upload .clay-seo-input { flex: 1; min-width: 0; }
72
+ .clay-seo-note { margin: 0; font-size: 12px; color: var(--bevel-muted); }
73
+ .clay-seo-more { grid-column: 1 / -1; border: 1px solid var(--bevel-line-2); background: var(--bevel-surface); }
74
+ .clay-seo-more > summary { position: relative; display: flex; align-items: center; gap: 9px; padding: 6px 10px 6px 8px; list-style: none; font-size: 12.5px; font-weight: 500; cursor: pointer; user-select: none; }
75
+ .clay-seo-more > summary::-webkit-details-marker { display: none; }
76
+ .clay-seo-more > summary::before { content: ""; box-sizing: border-box; width: 20px; height: 20px; border: 2px solid; border-color: var(--bevel-edge-hi) var(--bevel-edge-lo) var(--bevel-edge-lo) var(--bevel-edge-hi); background: var(--bevel-face); }
77
+ .clay-seo-more > summary::after { content: ""; position: absolute; left: 15px; top: 50%; width: 5px; height: 5px; border-right: 1.5px solid var(--bevel-ink); border-bottom: 1.5px solid var(--bevel-ink); transform: translateY(-50%) rotate(-45deg); }
78
+ .clay-seo-more[open] > summary { border-bottom: 1px solid var(--bevel-line); }
79
+ .clay-seo-more[open] > summary::before { background: var(--bevel-face-in); border-color: var(--bevel-edge-in) var(--bevel-edge-in-hi) var(--bevel-edge-in-hi) var(--bevel-edge-in); }
80
+ .clay-seo-more[open] > summary::after { transform: translate(1px, -65%) rotate(45deg); }
81
+ .clay-seo-more-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; padding: 10px; background: var(--bevel-sunk); }
82
+ .clay-seo-checks { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px 18px; }
83
+ .clay-seo-check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
84
+ .clay-seo-check input { appearance: none; -webkit-appearance: none; position: relative; box-sizing: border-box; flex: none; width: 16px; height: 16px; margin: 0; border: 2px solid; border-color: var(--bevel-edge-hi) var(--bevel-edge-lo) var(--bevel-edge-lo) var(--bevel-edge-hi); background: var(--bevel-face); cursor: pointer; }
85
+ .clay-seo-check input:checked { background: var(--bevel-face-in); border-color: var(--bevel-edge-in) var(--bevel-edge-in-hi) var(--bevel-edge-in-hi) var(--bevel-edge-in); }
86
+ .clay-seo-check input:checked::after { content: ""; position: absolute; left: 3px; top: 0; width: 4px; height: 8px; border-right: 1.8px solid var(--bevel-ink); border-bottom: 1.8px solid var(--bevel-ink); transform: rotate(42deg); }
87
+ .clay-seo-check input:focus-visible { outline: 2px solid var(--bevel-brass); outline-offset: 2px; }
88
+ .clay-seo-preview { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
89
+ .clay-seo-preview-head { display: flex; justify-content: space-between; align-items: baseline; font-size: 12.5px; font-weight: 500; color: var(--bevel-ink-2); }
90
+ .clay-seo-preview-head span { font: 400 11px/1 var(--bevel-font-mono); color: var(--bevel-muted); }
91
+ .clay-og { border: 1px solid var(--bevel-line-2); background: var(--bevel-ground); overflow: hidden; }
92
+ .clay-og-image { position: relative; display: flex; align-items: flex-end; aspect-ratio: 1.91 / 1; max-height: 160px; width: 100%; padding: 10px 12px; box-sizing: border-box; overflow: hidden; background: var(--og-image, var(--bevel-brass-soft)); border-bottom: 1px solid var(--bevel-line-2); }
93
+ .clay-og-image[hidden] { display: none; }
94
+ .clay-og-image span { position: relative; font: 500 10.5px/1.3 var(--bevel-font-mono); padding: 2px 7px; border: 1px solid var(--bevel-line-2); background: var(--bevel-surface); color: var(--bevel-ink); }
95
+ .clay-og-body { display: flex; flex-direction: column; gap: 2px; padding: 9px 12px 11px; }
96
+ .clay-og-host { font: 400 11px/1.3 var(--bevel-font-mono); color: var(--bevel-muted); }
97
+ .clay-og-title { font: 600 14px/1.3 var(--bevel-font-sans); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
98
+ .clay-og-desc { font-size: 12.5px; line-height: 1.4; color: var(--bevel-ink-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
99
+ .clay-seo-cardsize { margin: 0; font: 400 11px/1.3 var(--bevel-font-mono); color: var(--bevel-muted); }
100
+
101
+ @container (max-width: 680px) {
102
+ .clay-seo-inner { padding: 12px 14px 16px; }
103
+ .clay-seo-grid, .clay-seo-fields, .clay-seo-more-grid { grid-template-columns: 1fr; }
104
+ .clay-seo-input { font-size: 16px; min-height: 40px; }
105
+ textarea.clay-seo-input { min-height: 96px; }
106
+ textarea.clay-seo-input.is-short { min-height: 40px; }
107
+ .clay-seo-check { font-size: 15px; }
108
+ }
109
+ .clay-og-image img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
110
+ .clay-seo-check > .clay-seo-check { display: inline-block; }
111
+ .clay-seo-conflict { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 12px; color: var(--bevel-muted); }
112
+ .clay-seo-conflict[hidden] { display: none; }
113
+ .clay-seo-conflict p { margin: 0; flex-basis: 100%; }
114
+ .clay-seo-title-mode { margin: 0; font-size: 12px; color: var(--bevel-muted); }
115
+ @media (pointer: coarse), (max-width: 680px) { .clay-seo .clay-menu-btn { min-height: 40px; } .clay-seo :is(input, textarea, select) { font-size: 16px; } }
116
+ @media (prefers-reduced-motion: reduce) { .clay-seo { animation: none; transition: none; } }`;
288
117
 
289
118
  const text = (value) => (value == null ? "" : String(value));
290
119
 
@@ -295,15 +124,6 @@ function element(tag, className, content) {
295
124
  return el;
296
125
  }
297
126
 
298
- function stylesOnce() {
299
- if (document.getElementById(STYLE_ID)) return;
300
- const style = document.createElement("style");
301
- style.id = STYLE_ID;
302
- style.setAttribute("clay", "editor-ui");
303
- style.textContent = SHEET_CSS;
304
- document.head.append(style);
305
- }
306
-
307
127
  const parseUrl = (value) => {
308
128
  try {
309
129
  return new URL(value);
@@ -416,23 +236,40 @@ function showValue(key, value) {
416
236
  return raw || "(empty)";
417
237
  }
418
238
 
419
- function run(label, fn) {
420
- const undo = window.clay && window.clay.undo;
421
- if (undo && typeof undo.commit === "function") return undo.commit(label, fn);
422
- return fn();
239
+ function save() {
240
+ if (!document.documentElement.hasAttribute("autosave") && typeof window.clay?.save === "function") window.clay.save();
241
+ }
242
+
243
+ // `readHead` hands back what `applyHead` takes: a field's read and its write speak the
244
+ // same shape, booleans for index and follow and "custom"/"derived" for the title mode.
245
+ const restoreOf = (before) => ({ ...before });
246
+
247
+ function offerUndo(before) {
248
+ toastPersistent(APPLIED_TEXT, "success", {
249
+ actions: [
250
+ {
251
+ label: "Undo",
252
+ onClick: () => {
253
+ dismissPersistent(APPLIED_TEXT);
254
+ applyHead(document, restoreOf(before));
255
+ save();
256
+ },
257
+ },
258
+ ],
259
+ });
423
260
  }
424
261
 
425
- export function open() {
426
- if (document.querySelector(".clay-seo")) return;
427
- stylesOnce();
262
+ export function open({ returnFocus } = {}) {
263
+ if (document.querySelector('clay-editor-ui[data-clay-ui="seo"]')) return;
428
264
 
429
265
  const opened = readHead(document).values;
430
- const restoreTo = document.activeElement;
266
+ const restoreTo = returnFocus ?? deepActiveElement();
431
267
  const inputs = new Map();
432
268
  const errors = new Map();
433
269
  const conflicts = new Map();
434
270
  const chosen = new Set();
435
271
  let closed = false;
272
+ let ui = null;
436
273
  let uploadAvailable = false;
437
274
  let pendingTitleMode = "";
438
275
 
@@ -447,125 +284,170 @@ export function open() {
447
284
 
448
285
  const root = element("div", "clay-seo");
449
286
  root.setAttribute("clay", "editor-ui");
450
- root.setAttribute("role", "dialog");
451
- root.setAttribute("aria-modal", "true");
287
+ root.setAttribute("role", "region");
452
288
  root.setAttribute("aria-labelledby", "clay-seo-heading");
453
289
 
454
- const title = element("h2", "clay-seo-heading", LABEL);
455
- title.id = "clay-seo-heading";
456
- const closeButton = element("button", "clay-seo-x", "\u00d7");
457
- closeButton.type = "button";
458
- closeButton.setAttribute("aria-label", "Close");
459
- const top = element("div", "clay-seo-top");
460
- top.append(title, closeButton);
290
+ const derivedButton = element("button", "clay-seo-derived", "Use the first heading");
291
+ derivedButton.type = "button";
292
+ derivedButton.hidden = true;
461
293
 
462
- const previewImage = element("img", "clay-seo-preview-image");
463
- previewImage.alt = "";
464
- const previewTitle = element("p", "clay-seo-preview-title");
465
- const previewDescription = element("p", "clay-seo-preview-description");
466
- const previewHost = element("p", "clay-seo-preview-host");
467
- const previewText = element("div", "clay-seo-preview-text");
468
- previewText.append(previewTitle, previewDescription, previewHost);
469
- const preview = element("div", "clay-seo-preview");
470
- preview.append(previewImage, previewText);
294
+ const uploadButton = element("button", "clay-seo-upload-button clay-menu-btn", "Upload image");
295
+ uploadButton.type = "button";
296
+ const fileInput = element("input", "clay-seo-file");
297
+ fileInput.type = "file";
298
+ fileInput.accept = "image/*";
299
+ fileInput.hidden = true;
300
+ fileInput.tabIndex = -1;
301
+ const publicNote = element("p", "clay-seo-note", PUBLIC_NOTE);
302
+ publicNote.hidden = true;
303
+ const pasteNote = element("p", "clay-seo-note", PASTE_NOTE);
304
+ const uploadRow = element("div", "clay-seo-upload");
471
305
 
472
- const form = element("div", "clay-seo-form");
473
306
  const robotNote = element("p", "clay-seo-note");
474
307
  robotNote.hidden = true;
475
- const cardSize = element("p", "clay-seo-note");
308
+ const cardSize = element("p", "clay-seo-cardsize clay-seo-note");
476
309
 
477
- function buildField(field) {
310
+ const previewImage = element("img");
311
+ previewImage.alt = "";
312
+ const previewImageBox = element("div", "clay-og-image");
313
+ previewImageBox.hidden = true;
314
+ previewImageBox.append(previewImage);
315
+ const previewHost = element("span", "clay-og-host");
316
+ const previewTitle = element("span", "clay-og-title");
317
+ const previewDescription = element("span", "clay-og-desc");
318
+ const ogBody = element("div", "clay-og-body");
319
+ ogBody.append(previewHost, previewTitle, previewDescription);
320
+ const ogCard = element("div", "clay-og");
321
+ ogCard.append(previewImageBox, ogBody);
322
+ const previewState = element("span");
323
+ const previewHead = element("div", "clay-seo-preview-head");
324
+ previewHead.append("Link preview", previewState);
325
+ const preview = element("div", "clay-seo-preview");
326
+ preview.append(previewHead, ogCard, cardSize);
327
+
328
+ const form = element("div", "clay-seo-fields");
329
+ const counters = new Map();
330
+
331
+ function buildField(key) {
332
+ const spec = FIELD[key];
333
+ const id = `clay-seo-${key}`;
478
334
  const wrap = element("div", "clay-seo-field");
479
- const row = element("label", "clay-seo-row");
480
- const caption = element("span", "clay-seo-caption", field.label);
481
- const input =
482
- field.kind === "textarea" ? element("textarea", "clay-seo-input") : element("input", field.kind === "checkbox" ? "clay-seo-check" : "clay-seo-input");
483
- if (field.kind === "checkbox") {
484
- row.classList.add("clay-seo-row-check");
335
+ if (spec.full) wrap.classList.add("is-full");
336
+
337
+ const input = element(
338
+ spec.kind === "checkbox" ? "input" : spec.kind === "textarea" ? "textarea" : "input",
339
+ spec.kind === "checkbox" ? "clay-seo-check" : "clay-seo-input",
340
+ );
341
+ if (spec.kind === "checkbox") {
485
342
  input.type = "checkbox";
486
- input.checked = opened[field.key] !== false;
487
- row.append(input, caption);
343
+ input.checked = opened[key] !== false;
488
344
  } else {
489
- if (field.kind !== "textarea") input.type = "text";
490
- input.value = text(opened[field.key]);
491
- row.append(caption, input);
345
+ if (spec.kind !== "textarea") input.type = "text";
346
+ if (spec.short) {
347
+ input.rows = 1;
348
+ input.classList.add("is-short");
349
+ }
350
+ input.value = text(opened[key]);
492
351
  }
493
- input.dataset.key = field.key;
494
- if (window.clay && window.clay.unsent) input.setAttribute("unsent", `seo-${field.key}`);
495
- inputs.set(field.key, input);
352
+ if (spec.placeholder) input.placeholder = spec.placeholder;
353
+ input.id = id;
354
+ input.dataset.key = key;
355
+ if (window.clay && window.clay.unsent) input.setAttribute("unsent", `seo-${key}`);
356
+ inputs.set(key, input);
496
357
 
497
358
  const error = element("p", "clay-seo-error");
498
359
  error.hidden = true;
499
- errors.set(field.key, error);
360
+ errors.set(key, error);
500
361
 
501
362
  const conflict = element("div", "clay-seo-conflict");
502
363
  conflict.hidden = true;
503
364
  const theirsLine = element("p");
504
365
  const theirs = element("span", "clay-seo-theirs");
505
366
  theirsLine.append("Changed elsewhere: ", theirs);
506
- const theirsButton = element("button", "clay-seo-theirs-button", "Use theirs");
367
+ const theirsButton = element("button", "clay-menu-btn clay-menu-btn-small clay-seo-theirs-button", "Use theirs");
507
368
  theirsButton.type = "button";
508
- const mineButton = element("button", "clay-seo-mine-button", "Keep mine");
369
+ const mineButton = element("button", "clay-menu-btn clay-menu-btn-small clay-seo-mine-button", "Keep mine");
509
370
  mineButton.type = "button";
510
371
  conflict.append(theirsLine, theirsButton, mineButton);
511
- conflicts.set(field.key, { row: conflict, theirs });
372
+ conflicts.set(key, { row: conflict, theirs });
512
373
 
513
374
  theirsButton.addEventListener("click", () => {
514
- const value = readHead(document).values[field.key];
515
- if (field.kind === "checkbox") input.checked = value !== false;
375
+ const value = readHead(document).values[key];
376
+ if (spec.kind === "checkbox") input.checked = value !== false;
516
377
  else input.value = text(value);
517
- chosen.add(field.key);
378
+ chosen.add(key);
518
379
  conflict.hidden = true;
519
380
  refresh();
520
381
  });
521
382
  mineButton.addEventListener("click", () => {
522
- chosen.add(field.key);
383
+ chosen.add(key);
523
384
  conflict.hidden = true;
524
385
  });
525
386
 
526
- wrap.append(row, error, conflict);
527
- return wrap;
528
- }
387
+ if (spec.kind === "checkbox") {
388
+ const label = element("label", "clay-seo-check");
389
+ label.append(input, spec.label);
390
+ wrap.append(label, error, conflict);
391
+ return wrap;
392
+ }
529
393
 
530
- for (const group of GROUPS) {
531
- const section = element("fieldset", "clay-seo-group");
532
- section.append(element("legend", "clay-seo-legend", group.legend));
533
- for (const field of group.fields) section.append(buildField(field));
534
- if (group.legend === "Search") section.append(robotNote);
535
- if (group.legend === "Social card") section.append(cardSize);
536
- form.append(section);
394
+ const row = element("div", "clay-seo-label-row");
395
+ const label = element("label");
396
+ label.htmlFor = id;
397
+ label.append(spec.label);
398
+ if (spec.note) label.append(" ", element("span", null, `(${spec.note})`));
399
+ row.append(label);
400
+ if (spec.count) {
401
+ const count = element("span", "clay-seo-count");
402
+ counters.set(key, { el: count, max: spec.count });
403
+ const aside = element("span", "clay-seo-aside");
404
+ if (key === "title") aside.append(derivedButton);
405
+ aside.append(count);
406
+ row.append(aside);
407
+ }
408
+ wrap.append(row);
409
+ if (key === "ogImage") {
410
+ uploadRow.append(input, uploadButton, fileInput);
411
+ wrap.append(uploadRow, pasteNote, publicNote);
412
+ } else {
413
+ wrap.append(input);
414
+ }
415
+ wrap.append(error, conflict);
416
+ return wrap;
537
417
  }
538
418
 
539
- const derivedButton = element("button", "clay-seo-derived", "Use the first heading");
540
- derivedButton.type = "button";
541
- derivedButton.hidden = true;
542
- inputs.get("title").closest(".clay-seo-field").append(derivedButton);
419
+ for (const key of BASE_KEYS) form.append(buildField(key));
543
420
 
544
- const imageInput = inputs.get("ogImage");
545
- const uploadButton = element("button", "clay-seo-upload-button", "Upload image");
546
- uploadButton.type = "button";
547
- const fileInput = element("input", "clay-seo-file");
548
- fileInput.type = "file";
549
- fileInput.accept = "image/*";
550
- fileInput.hidden = true;
551
- fileInput.tabIndex = -1;
552
- const publicNote = element("p", "clay-seo-note", PUBLIC_NOTE);
553
- publicNote.hidden = true;
554
- const pasteNote = element("p", "clay-seo-note", PASTE_NOTE);
555
- const uploadRow = element("div", "clay-seo-upload");
556
- uploadRow.append(uploadButton, fileInput, pasteNote, publicNote);
557
- imageInput.closest(".clay-seo-field").append(uploadRow);
421
+ const more = element("details", "clay-seo-more");
422
+ more.append(element("summary", null, "More search and social fields"));
423
+ const moreGrid = element("div", "clay-seo-more-grid");
424
+ for (const key of MORE_KEYS) moreGrid.append(buildField(key));
425
+ const checks = element("div", "clay-seo-checks");
426
+ for (const key of CHECK_KEYS) checks.append(buildField(key));
427
+ checks.append(robotNote);
428
+ moreGrid.append(checks);
429
+ more.append(moreGrid);
430
+ form.append(more);
558
431
 
559
- const editButton = element("button", "clay-seo-edit", "Edit these settings");
432
+ const editButton = element("button", "clay-seo-edit clay-menu-btn", "Edit these settings");
560
433
  editButton.type = "button";
561
- const cancelButton = element("button", "clay-seo-cancel", "Cancel");
434
+ const cancelButton = element("button", "clay-seo-cancel clay-menu-btn", "Cancel");
562
435
  cancelButton.type = "button";
563
- const applyButton = element("button", "clay-seo-apply", "Apply");
436
+ const applyButton = element("button", "clay-seo-apply clay-menu-btn clay-menu-btn-primary", "Done");
564
437
  applyButton.type = "button";
565
438
  const actions = element("div", "clay-seo-actions");
566
439
  actions.append(editButton, cancelButton, applyButton);
567
440
 
568
- root.append(top, preview, form, actions);
441
+ const heading = element("h2", "clay-seo-heading", LABEL);
442
+ heading.id = "clay-seo-heading";
443
+ const bar = element("div", "clay-seo-bar");
444
+ bar.append(heading, actions);
445
+
446
+ const grid = element("div", "clay-seo-grid");
447
+ grid.append(form, preview);
448
+ const inner = element("div", "clay-seo-inner");
449
+ inner.append(bar, grid);
450
+ root.append(inner);
569
451
 
570
452
  const readField = (key) => {
571
453
  const input = inputs.get(key);
@@ -595,6 +477,13 @@ export function open() {
595
477
  inputs.get("ogTitle").placeholder = titleValue;
596
478
  inputs.get("ogDescription").placeholder = descriptionValue;
597
479
 
480
+ for (const [key, entry] of counters) {
481
+ const length = text(readField(key)).trim().length;
482
+ entry.el.textContent = `${length} / ${entry.max}`;
483
+ if (length > entry.max) entry.el.dataset.over = "";
484
+ else delete entry.el.dataset.over;
485
+ }
486
+
598
487
  const image = imageSrc();
599
488
  if (image) {
600
489
  previewImage.src = image;
@@ -603,12 +492,14 @@ export function open() {
603
492
  previewImage.removeAttribute("src");
604
493
  previewImage.hidden = true;
605
494
  }
495
+ previewImageBox.hidden = previewImage.hidden;
606
496
  previewTitle.textContent = text(readField("ogTitle")).trim() || titleValue || "Untitled page";
607
497
  const description = text(readField("ogDescription")).trim() || descriptionValue;
608
498
  previewDescription.textContent = description;
609
499
  previewDescription.hidden = !description;
610
500
  previewHost.textContent = hostOf(readField("canonical")) || location.host || "";
611
- cardSize.textContent = image ? "Card size: Summary with a large image" : "Card size: Summary";
501
+ previewState.textContent = image ? "Large image card \u00b7 1.91:1" : "Summary card";
502
+ cardSize.textContent = image ? "Shared links show the large image." : "Add an image for the large card.";
612
503
  derivedButton.hidden = !(isDocumentPage() && FIELDS.titleMode.read(document) === "custom");
613
504
  }
614
505
 
@@ -646,15 +537,29 @@ export function open() {
646
537
  return found;
647
538
  }
648
539
 
540
+ function revealMore(found) {
541
+ for (const key of Object.keys(found)) {
542
+ const input = inputs.get(key);
543
+ if (input && more.contains(input)) {
544
+ more.open = true;
545
+ return;
546
+ }
547
+ }
548
+ }
549
+
649
550
  function showProblems(found) {
551
+ revealMore(found);
650
552
  for (const [key, el] of errors) {
651
553
  const message = found[key] || "";
652
554
  el.textContent = message;
653
555
  el.hidden = !message;
556
+ if (message) inputs.get(key)?.setAttribute("aria-invalid", "true");
557
+ else inputs.get(key)?.removeAttribute("aria-invalid");
654
558
  }
655
559
  }
656
560
 
657
561
  function showConflicts(found) {
562
+ revealMore(found);
658
563
  for (const [key, entry] of conflicts) {
659
564
  const current = found[key];
660
565
  const visible = current !== undefined;
@@ -664,6 +569,10 @@ export function open() {
664
569
  }
665
570
 
666
571
  function apply() {
572
+ if (!(window.clay && window.clay.mode === "edit")) {
573
+ close();
574
+ return;
575
+ }
667
576
  const wanted = changes();
668
577
  const documentPage = isDocumentPage();
669
578
  const marker = documentPage ? pendingTitleMode || ("title" in wanted ? "custom" : "") : "";
@@ -683,14 +592,26 @@ export function open() {
683
592
  showConflicts(found);
684
593
  return;
685
594
  }
686
- run(LABEL, () => applyHead(document, marker ? { ...wanted, titleMode: marker } : wanted));
595
+ const next = marker ? { ...wanted, titleMode: marker } : wanted;
596
+ const undo = window.clay && window.clay.undo;
597
+ if (undo && typeof undo.commit === "function") {
598
+ undo.commit(UNDO_LABEL, () => applyHead(document, next));
599
+ } else {
600
+ const before = readHead(document).values;
601
+ applyHead(document, next);
602
+ offerUndo(before);
603
+ }
687
604
  close();
605
+ // Apply is an explicit save of these settings: a page that does not autosave its markup
606
+ // (the page editor saves only its own stores) would otherwise keep them until edit ends.
607
+ save();
688
608
  }
689
609
 
690
610
  function applyUploadVisibility() {
691
- uploadButton.hidden = !uploadAvailable;
692
- pasteNote.hidden = uploadAvailable;
693
- publicNote.hidden = !uploadAvailable || !/(^|\.)hyperclay\.com$/i.test(location.hostname);
611
+ const editing = !!(window.clay && window.clay.mode === "edit");
612
+ uploadButton.hidden = !uploadAvailable || !editing;
613
+ pasteNote.hidden = uploadAvailable || !editing;
614
+ publicNote.hidden = !uploadAvailable || !editing || !/(^|\.)hyperclay\.com$/i.test(location.hostname);
694
615
  }
695
616
 
696
617
  function layout() {
@@ -701,15 +622,16 @@ export function open() {
701
622
  }
702
623
  editButton.hidden = editing;
703
624
  cancelButton.hidden = !editing;
704
- applyButton.hidden = !editing;
705
- uploadRow.hidden = !editing || !uploadAvailable;
706
625
  applyUploadVisibility();
707
626
  }
708
627
 
709
628
  // A live entry commits edit without this page going anywhere, so the sheet's own
710
629
  // read-only layout has to follow the mode rather than the mode it opened in.
711
630
  function followMode() {
712
- if (!closed) layout();
631
+ if (closed) return;
632
+ layout();
633
+ // Uploads load with edit, so a sheet opened in view asks again once edit is live.
634
+ if (!uploadAvailable) checkUploads();
713
635
  }
714
636
 
715
637
  function close() {
@@ -717,7 +639,9 @@ export function open() {
717
639
  closed = true;
718
640
  pending.dispose();
719
641
  document.removeEventListener("clay:modechange", followMode);
720
- root.remove();
642
+ if (heightObserver) heightObserver.disconnect();
643
+ heightVars.remove();
644
+ ui?.remove();
721
645
  if (restoreTo && restoreTo.isConnected && typeof restoreTo.focus === "function") restoreTo.focus();
722
646
  }
723
647
 
@@ -749,12 +673,6 @@ export function open() {
749
673
  layout();
750
674
  }
751
675
 
752
- function focusables() {
753
- return Array.from(root.querySelectorAll("button, input, textarea")).filter(
754
- (el) => !el.disabled && !el.hidden && el.tabIndex >= 0 && !el.closest("[hidden]"),
755
- );
756
- }
757
-
758
676
  form.addEventListener("input", () => {
759
677
  refresh();
760
678
  pending.changed();
@@ -794,7 +712,6 @@ export function open() {
794
712
 
795
713
  applyButton.addEventListener("click", apply);
796
714
  cancelButton.addEventListener("click", close);
797
- closeButton.addEventListener("click", close);
798
715
 
799
716
  editButton.addEventListener("click", async () => {
800
717
  const clay = window.clay;
@@ -815,32 +732,34 @@ export function open() {
815
732
  if (reloading) return;
816
733
  if (storage) storage.remove(REOPEN_KEY);
817
734
  const refusal = modeRefusalText(result);
818
- if (refusal) window.clay?.toast?.(refusal, "error");
735
+ if (refusal) (window.clay?.menu?.note || window.clay?.toast)?.(refusal, "error");
819
736
  if (result && result.ok !== false) layout();
820
737
  });
821
738
 
739
+ const heightVars = document.createElement("style");
740
+ heightVars.id = "clay-seo-vars";
741
+ heightVars.setAttribute("clay", "editor-ui");
742
+ document.head.append(heightVars);
743
+ let heightObserver = null;
744
+
745
+ function writeHeight() {
746
+ heightVars.textContent = `:root { --clay-seo-height: ${Math.ceil(root.getBoundingClientRect().height)}px; }`;
747
+ }
748
+
822
749
  root.addEventListener("keydown", (event) => {
823
- if (event.key === "Escape") {
824
- event.preventDefault();
825
- close();
826
- return;
827
- }
828
- if (event.key !== "Tab") return;
829
- const all = focusables();
830
- if (!all.length) return;
831
- const first = all[0];
832
- const last = all[all.length - 1];
833
- if (event.shiftKey && document.activeElement === first) {
834
- event.preventDefault();
835
- last.focus();
836
- } else if (!event.shiftKey && document.activeElement === last) {
837
- event.preventDefault();
838
- first.focus();
839
- }
750
+ if (event.key !== "Escape") return;
751
+ event.preventDefault();
752
+ close();
840
753
  });
841
754
 
842
755
  document.addEventListener("clay:modechange", followMode);
843
- document.body.append(root);
756
+ lookAt(root, { x: 0, y: 0, width: innerWidth, height: Math.min(200, innerHeight) }, MENU_SURFACE);
757
+ ui = mountEditorUI({ name: "seo", content: root, css: [MENU_CSS, SHEET_CSS], before: document.body.firstChild });
758
+ writeHeight();
759
+ if (typeof ResizeObserver === "function") {
760
+ heightObserver = new ResizeObserver(writeHeight);
761
+ heightObserver.observe(root);
762
+ }
844
763
  refresh();
845
764
  layout();
846
765
  inputs.get("title").focus();