@panphora/clayjs 1.21.0 → 1.23.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 (192) 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 +8871 -8629
  11. package/dist/m/attrs/{save-freeze-MTFVVYV5.js → save-freeze-JZHKRQLR.js} +1 -1
  12. package/dist/m/{c-OABVRYOT.js → c-24PMUJIV.js} +1 -1
  13. package/dist/m/{c-EOCZ2YVL.js → c-2AYVYEI4.js} +1 -1
  14. package/dist/m/{c-UXEVJ63W.js → c-2L6O5PDS.js} +1 -1
  15. package/dist/m/{c-VBLFAQD2.js → c-2RYOGUQM.js} +1 -1
  16. package/dist/m/{c-LDUNOR57.js → c-2Z22FCFK.js} +13 -25
  17. package/dist/m/{c-NLZ5AN4L.js → c-2ZVOEU4M.js} +1 -1
  18. package/dist/m/{c-BJPZCWAV.js → c-37NAONZA.js} +1 -1
  19. package/dist/m/{c-ZE46QDIH.js → c-3TLG3OLG.js} +1 -1
  20. package/dist/m/{c-CSNE5EIF.js → c-4PO3YGDC.js} +1 -1
  21. package/dist/m/{c-OJYM642Q.js → c-5FKRZNJV.js} +1 -1
  22. package/dist/m/{c-SO63UGPQ.js → c-5OJ7YF2N.js} +1 -1
  23. package/dist/m/{c-CAWNFER6.js → c-5TIZ2I64.js} +1 -1
  24. package/dist/m/{c-7SXYQCN4.js → c-5WMXEKFH.js} +1 -1
  25. package/dist/m/{c-XQSXKIKM.js → c-6RDOKLN7.js} +1 -1
  26. package/dist/m/{c-BRLCTNMC.js → c-6TYGD5AB.js} +1 -1
  27. package/dist/m/c-A4IP6MBC.js +1 -0
  28. package/dist/m/{c-I2IFKJX2.js → c-ALDVFPWL.js} +1 -1
  29. package/dist/m/{c-LV5MABYD.js → c-AYVGD6A6.js} +1 -1
  30. package/dist/m/{c-LQU7TXIH.js → c-B6O7AJYG.js} +1 -1
  31. package/dist/m/{c-SBSMJIPC.js → c-BCGWJYXW.js} +1 -1
  32. package/dist/m/{c-RT3C74GY.js → c-BGKAT2FL.js} +1 -1
  33. package/dist/m/{c-N7Y52HJH.js → c-BXYSDKAN.js} +1 -1
  34. package/dist/m/c-CRPIDGXC.js +78 -0
  35. package/dist/m/c-CUMXGHVD.js +1 -0
  36. package/dist/m/{c-67ILSNPR.js → c-DTMZ3ZHI.js} +1 -1
  37. package/dist/m/{c-EZ7B5AZT.js → c-ELYIIYRS.js} +1 -1
  38. package/dist/m/{c-VAK7SG37.js → c-EUGJVHDD.js} +1 -1
  39. package/dist/m/{c-67LGZ52V.js → c-FCSZ3DC5.js} +1 -1
  40. package/dist/m/{c-JUE7B4OX.js → c-FQGHR32O.js} +1 -1
  41. package/dist/m/{c-ZJLU56DV.js → c-G4PWUDD5.js} +1 -1
  42. package/dist/m/{c-CJFDLD43.js → c-G6COVIZB.js} +1 -1
  43. package/dist/m/c-GBTVCJXC.js +1 -0
  44. package/dist/m/c-GC2HICFJ.js +1 -0
  45. package/dist/m/{c-44B5ODN7.js → c-HCNWJE4A.js} +2 -2
  46. package/dist/m/{c-BJ7YQ5QZ.js → c-IHUWUKIU.js} +1 -1
  47. package/dist/m/{c-LIBE4HPD.js → c-JCOQVZYS.js} +1 -1
  48. package/dist/m/{c-RG6XUZFA.js → c-JGHEACGK.js} +1 -1
  49. package/dist/m/{c-3GJ4XARY.js → c-JGLX6TBK.js} +1 -1
  50. package/dist/m/c-JNRKHYWO.js +1 -0
  51. package/dist/m/{c-PIVIYLTY.js → c-KDXIJEEF.js} +1 -1
  52. package/dist/m/{c-NFR673TL.js → c-L4KMT7FO.js} +2 -2
  53. package/dist/m/{c-E4IBSAQM.js → c-LCYPMULG.js} +1 -1
  54. package/dist/m/{c-SJPD46ZO.js → c-LIGQYZ7M.js} +4 -4
  55. package/dist/m/{c-U5X57T4L.js → c-LKIWEPAV.js} +1 -1
  56. package/dist/m/{c-EDSPKYMA.js → c-LUXAB2XI.js} +1 -1
  57. package/dist/m/{c-YSB6JKV2.js → c-LWIDDWAR.js} +2 -2
  58. package/dist/m/c-MPSKIU2I.js +1 -0
  59. package/dist/m/{c-DHXPDLW6.js → c-NK33MMYF.js} +1 -1
  60. package/dist/m/{c-GPGYKER7.js → c-NQLWOHLH.js} +1 -1
  61. package/dist/m/{c-LPUJXRXW.js → c-NTHUTABN.js} +1 -1
  62. package/dist/m/{c-Y33LUUIQ.js → c-NW54NHKE.js} +1 -1
  63. package/dist/m/{c-3AXJDL2P.js → c-OG6GFSUX.js} +1 -1
  64. package/dist/m/{c-MTQHWQC3.js → c-OKEGUICD.js} +1 -1
  65. package/dist/m/c-OPONJ2UK.js +128 -0
  66. package/dist/m/{c-3YH6JSIL.js → c-OWQWOGYI.js} +1 -1
  67. package/dist/m/{c-FDAXZ2PM.js → c-P6Q2ZTFQ.js} +1 -1
  68. package/dist/m/c-PKCCHJ5X.js +1 -0
  69. package/dist/m/c-PWJSJEJU.js +1 -0
  70. package/dist/m/{c-2ZZBC45O.js → c-PXLTUYZC.js} +1 -1
  71. package/dist/m/c-QEN6HZCA.js +1 -0
  72. package/dist/m/{c-JOH753EJ.js → c-QGAPG7OM.js} +1 -1
  73. package/dist/m/{c-W23KVGUO.js → c-QIR5CAKM.js} +1 -1
  74. package/dist/m/{c-GKEATME6.js → c-QM5LXRDT.js} +1 -1
  75. package/dist/m/c-QUGCKMWF.js +1 -0
  76. package/dist/m/{c-FNVQAACK.js → c-QYYUVLVG.js} +1 -1
  77. package/dist/m/c-RO73MKKR.js +1 -0
  78. package/dist/m/c-SBCBXKOZ.js +26 -0
  79. package/dist/m/{c-J3BXFTZS.js → c-SGRIYOAJ.js} +1 -1
  80. package/dist/m/c-SKF5HL4I.js +4 -0
  81. package/dist/m/{c-W3K23S72.js → c-SRPC3VBE.js} +1 -1
  82. package/dist/m/{c-K76AUL4I.js → c-T5NK2TVY.js} +1 -1
  83. package/dist/m/{c-EFD6QNTE.js → c-TANBH7FI.js} +1 -1
  84. package/dist/m/{c-5734MF2P.js → c-THGQQDYY.js} +1 -1
  85. package/dist/m/{c-YQUJ74R3.js → c-TSBVMWAH.js} +1 -1
  86. package/dist/m/{c-VY6XRBOW.js → c-UD5JZTDL.js} +1 -1
  87. package/dist/m/{c-M4FSPNQ2.js → c-UDGEFWNU.js} +1 -1
  88. package/dist/m/{c-ZSCBEB7I.js → c-UHUNXWKN.js} +1 -1
  89. package/dist/m/{c-BTLFLLML.js → c-VIRFF5HG.js} +1 -1
  90. package/dist/m/{c-2VB555FG.js → c-VYJ7O4RI.js} +1 -1
  91. package/dist/m/{c-TUGVINJR.js → c-W5E5P2TZ.js} +1 -1
  92. package/dist/m/{c-EOCKLMKX.js → c-W7IPVCAA.js} +1 -1
  93. package/dist/m/{c-5FEU5MMY.js → c-WJ5DRBHT.js} +1 -1
  94. package/dist/m/{c-V2J5C5MT.js → c-WQZTI5P2.js} +1 -1
  95. package/dist/m/c-X5WC4PX5.js +1 -0
  96. package/dist/m/{c-GVPNDIKJ.js → c-XCFQEWW6.js} +2 -2
  97. package/dist/m/c-XLQ3BDMM.js +1 -0
  98. package/dist/m/{c-XD5MOROJ.js → c-XX75FFNL.js} +1 -1
  99. package/dist/m/{c-JHJR5BQX.js → c-YK2L436K.js} +1 -1
  100. package/dist/m/{c-MEWUGR4D.js → c-YYMZWQLR.js} +1 -1
  101. package/dist/m/core/{admin-attrs-ZG7R5V3E.js → admin-attrs-U7IJNYBG.js} +1 -1
  102. package/dist/m/core/{autosave-VPORYJSE.js → autosave-F5YQCKVL.js} +1 -1
  103. package/dist/m/core/{conflict-notice-JMAZLVRX.js → conflict-notice-42H4ASLA.js} +1 -1
  104. package/dist/m/core/{persist-3OPWJKZS.js → persist-CJXIL7D4.js} +1 -1
  105. package/dist/m/core/{save-AOJBDSOK.js → save-ESPSQPLV.js} +1 -1
  106. package/dist/m/core/{save-core-IOCMXO5L.js → save-core-6SQHOR6W.js} +1 -1
  107. package/dist/m/core/snapshot-FJP5IJL3.js +1 -0
  108. package/dist/m/core/{unsaved-warning-U5VUJFOE.js → unsaved-warning-HNQVTA2D.js} +1 -1
  109. package/dist/m/internals/{index-HDRANDWE.js → index-NRPNDTSA.js} +1 -1
  110. package/dist/m/loader-FBDE2UAB.js +1 -0
  111. package/dist/m/manifest-1D2F5CA5.js +1 -0
  112. package/dist/m/manifest-edit-F7CC5CFD.js +1 -0
  113. package/dist/m/manifest.json +1139 -1069
  114. package/dist/m/page/{editor-EY2WAAGB.js → editor-OSKOW6RS.js} +2 -2
  115. package/dist/m/plugins/ai-edit-VV4QTINH.js +6 -0
  116. package/dist/m/plugins/cms-H7DTMUQX.js +2235 -0
  117. package/dist/m/plugins/{indicator-4VDXWYTD.js → indicator-WVPGMOZ7.js} +1 -1
  118. package/dist/m/plugins/{live-5EZN2EYP.js → live-DCUBKXY4.js} +1 -1
  119. package/dist/m/plugins/menu-Q4MFFQ6E.js +1 -0
  120. package/dist/m/plugins/{page-LVW5DWA4.js → page-OAWUJQEL.js} +2 -2
  121. package/dist/m/plugins/{page-view-5KQ76IJB.js → page-view-DZ26ZDDQ.js} +1 -1
  122. package/dist/m/plugins/{people-C7TVBBMR.js → people-5XRNRP5U.js} +1 -1
  123. package/dist/m/plugins/{quickcrop-BQ5YYFCH.js → quickcrop-WWF5I422.js} +2 -2
  124. package/dist/m/plugins/{richclay-KLGETK77.js → richclay-PGOOJ4OK.js} +2 -2
  125. package/dist/m/plugins/{sortable-WJZZBM7R.js → sortable-266HY6TR.js} +1 -1
  126. package/dist/m/plugins/{source-CCEUM3OR.js → source-6BKMK4PO.js} +1 -1
  127. package/dist/m/plugins/{upgrade-NW556WJD.js → upgrade-BNZVFHMZ.js} +1 -1
  128. package/dist/m/plugins/visitor-drafts-2AUYKAIJ.js +20 -0
  129. package/dist/m/plugins/{visitor-drafts-owner-MZP45XSG.js → visitor-drafts-owner-Z5B2OQBR.js} +1 -1
  130. package/dist/m/plugins/visitor-menu-4LQMKLPP.js +1 -0
  131. package/dist/m/plugins/{wire-VRYPY6EJ.js → wire-W2EV36KZ.js} +1 -1
  132. package/dist/m/{reader-core-2GXY7SHY.js → reader-core-POFQCKRV.js} +2 -2
  133. package/dist/m/sync/{live-sync-SKPMFYOO.js → live-sync-XGUPHI3I.js} +1 -1
  134. package/dist/m/ui/{index-QNIPDHXQ.js → index-VGYAKAKI.js} +1 -1
  135. package/dist/offline-manifest.json +666 -606
  136. package/docs/guide.md +38 -27
  137. package/package.json +2 -2
  138. package/src/core/save-replace.js +5 -2
  139. package/src/core/serialize-steps.js +10 -2
  140. package/src/core/snapshot.js +37 -1
  141. package/src/core/upgrade-run.js +16 -2
  142. package/src/loader-logic.js +10 -3
  143. package/src/plugins/ai-edit.js +6 -17
  144. package/src/plugins/cms.js +3 -6
  145. package/src/plugins/menu/dom-items.js +45 -0
  146. package/src/plugins/menu/looks.js +32 -0
  147. package/src/plugins/menu/shell-style.js +143 -0
  148. package/src/plugins/menu/shell.js +398 -0
  149. package/src/plugins/menu/style.js +15 -116
  150. package/src/plugins/menu/variants.js +56 -10
  151. package/src/plugins/menu/visitor.js +78 -0
  152. package/src/plugins/menu.js +21 -313
  153. package/src/plugins/visitor-drafts/inert.js +1 -0
  154. package/src/plugins/visitor-drafts/intro-style.js +6 -0
  155. package/src/plugins/visitor-drafts/intro.js +77 -25
  156. package/src/plugins/visitor-drafts/owner-chip.js +18 -23
  157. package/src/plugins/visitor-drafts/prompt.js +2 -2
  158. package/src/plugins/visitor-drafts/store.js +58 -5
  159. package/src/plugins/visitor-drafts.js +1 -0
  160. package/src/plugins/visitor-menu.js +13 -0
  161. package/src/sync/live-sync.js +2 -1
  162. package/src/sync/page-relay.js +2 -2
  163. package/src/ui/bevel.js +1 -1
  164. package/src/ui/page-colour.js +17 -9
  165. package/src/ui/skins/cms.js +1 -1
  166. package/src/ui/skins/quickcrop.js +1 -1
  167. package/src/ui/skins/richclay.js +1 -1
  168. package/src/vendor/hypercms.vendor.js +16 -69
  169. package/dist/m/c-AL4RBJF2.js +0 -1
  170. package/dist/m/c-DDDOKPI6.js +0 -3
  171. package/dist/m/c-EGTIAL6P.js +0 -1
  172. package/dist/m/c-FSWRGBVQ.js +0 -1
  173. package/dist/m/c-HDWZEJHM.js +0 -183
  174. package/dist/m/c-HMYWIHFG.js +0 -1
  175. package/dist/m/c-JH5FF4NL.js +0 -20
  176. package/dist/m/c-M6FNQCTQ.js +0 -1
  177. package/dist/m/c-MHI3GYKF.js +0 -1
  178. package/dist/m/c-MIL4B2JQ.js +0 -1
  179. package/dist/m/c-S5IEIZCN.js +0 -1
  180. package/dist/m/c-VSPAFOKU.js +0 -1
  181. package/dist/m/c-WFMRX5RZ.js +0 -1
  182. package/dist/m/core/snapshot-O7II2AGG.js +0 -1
  183. package/dist/m/loader-ZVPK4ZN3.js +0 -1
  184. package/dist/m/manifest-5498AFD3.js +0 -1
  185. package/dist/m/manifest-edit-62A5A00F.js +0 -1
  186. package/dist/m/plugins/ai-edit-J5VLSW5I.js +0 -6
  187. package/dist/m/plugins/cms-LRAOZHT7.js +0 -2293
  188. package/dist/m/plugins/menu-DM2JHPSR.js +0 -1
  189. package/dist/m/plugins/visitor-drafts-2AWNT23K.js +0 -20
  190. package/src/ui/skins/cms-toggle.js +0 -15
  191. /package/dist/m/plugins/{quickcrop-BQ5YYFCH.js.LEGAL.txt → quickcrop-WWF5I422.js.LEGAL.txt} +0 -0
  192. /package/dist/m/plugins/{richclay-KLGETK77.js.LEGAL.txt → richclay-PGOOJ4OK.js.LEGAL.txt} +0 -0
@@ -1,60 +1,20 @@
1
1
  // The menu's look: Bevel tokens scoped to the menu family, solid surfaces, one soft
2
2
  // shadow, light or dark by the scheme ui/page-colour.js reads from the page behind it.
3
+ import { SHELL_A, SHELL_B, SHELL_C, SHELL_FLOATING, MENU_SKIN, MENU_TOUCH } from "./shell-style.js";
4
+ export { MENU_SKIN, MENU_TOUCH } from "./shell-style.js";
5
+
3
6
  // The surface and muted ink below, per scheme, for ui/page-colour.js's tint.
4
7
  export const MENU_SURFACE = { light: [0xFF, 0xFC, 0xF6], dark: [0x1A, 0x1D, 0x2C], mutedLight: [0x6B, 0x59, 0x42], mutedDark: [0xA3, 0xA9, 0xC2] };
5
8
 
6
9
  // The parts of the look a page may replace, through <style clay-menu-skin="1">. Everything
7
- // else below stays: layout, hidden, focus rings, touch targets and reduced motion.
8
- export const MENU_BASE = `.clay-menu-dock, .clay-menu-spots, .clay-seo {
9
- --bevel-font-sans: system-ui, -apple-system, "Segoe UI", sans-serif;
10
- --bevel-font-serif: Newsreader, Georgia, "Times New Roman", serif;
11
- --bevel-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
12
- --bevel-ground: light-dark(#FDF8F0, #0B0C13);
13
- --bevel-surface: light-dark(#FFFCF6, #1A1D2C);
14
- --bevel-sunk: light-dark(#F4ECDF, #22263A);
15
- --bevel-ink: light-dark(#2B241B, #ECEAF2);
16
- --bevel-ink-2: light-dark(#4A4033, #C9CDE0);
17
- --bevel-muted: light-dark(#6B5942, #A3A9C2);
18
- --bevel-faint: light-dark(#9A8A70, #6A7090);
19
- --bevel-line: light-dark(#E8DCC6, #2C3149);
20
- --bevel-line-2: light-dark(#D8C8AF, #3A4058);
21
- --bevel-line-3: light-dark(#C4B192, #4A536F);
22
- --bevel-brass: light-dark(#8F6310, #D9A445);
23
- --bevel-brass-soft: light-dark(#F0E4CC, #3A2F17);
24
- --bevel-ox: light-dark(#A83A2C, #FF6B79);
25
- --bevel-ox-soft: light-dark(#F6DED6, #3A2226);
26
- --bevel-teal: light-dark(#256056, #4E9C8C);
27
- --bevel-teal-soft: light-dark(#DCEBE7, #17332E);
28
- --bevel-face: light-dark(#F1E7D4, #2A2F48);
29
- --bevel-edge-hi: light-dark(#FFF9ED, #454C6C);
30
- --bevel-edge-lo: light-dark(#D6C3A5, #171B2C);
31
- --bevel-face-in: light-dark(#E3D0B3, #20253B);
32
- --bevel-edge-in: light-dark(#B89F7B, #12152A);
33
- --bevel-edge-in-hi: light-dark(#F0E1C9, #4D587A);
34
- --bevel-knob: light-dark(#FCF8F1, #5A6286);
35
- --bevel-knob-hi: light-dark(#FFF9ED, #7A83A8);
36
- --clay-shadow: 0 1px 2px light-dark(rgb(43 36 27 / .07), rgb(0 0 0 / .35)), 0 10px 28px -10px light-dark(rgb(43 36 27 / .32), rgb(0 0 0 / .7));
37
- --clay-sheet-shadow: 0 16px 30px -22px light-dark(rgb(43 36 27 / .45), rgb(0 0 0 / .85));
38
- color-scheme: inherit;
39
- font-family: var(--bevel-font-sans);
40
- color: var(--bevel-ink);
41
- -webkit-text-size-adjust: 100%;
42
- text-size-adjust: 100%;
43
- }
44
-
45
- .clay-menu-dock { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 1.4; }
46
- .clay-menu-dock[data-embedded] { position: relative; display: inline-flex; }
47
- .clay-menu-dock[hidden], .clay-menu-spots[hidden], .clay-menu-dock [hidden], .clay-seo [hidden] { display: none !important; }
48
-
49
- .clay-menu { box-sizing: border-box; display: inline-flex; align-items: center; gap: 2px; user-select: none; -webkit-user-select: none; }
50
-
51
- .clay-menu-edit { position: relative; display: inline-flex; align-items: center; gap: 8px; height: 28px; margin: 0; padding: 0 8px 0 5px; border: 0; background: none; font: 500 13px/1 var(--bevel-font-sans); color: var(--bevel-ink); cursor: pointer; }
10
+ // else stays: layout, hidden, focus rings, touch targets and reduced motion.
11
+ const OWNER_1 = `.clay-menu-edit { position: relative; display: inline-flex; align-items: center; gap: 8px; height: 28px; margin: 0; padding: 0 8px 0 5px; border: 0; background: none; font: 500 13px/1 var(--bevel-font-sans); color: var(--bevel-ink); cursor: pointer; }
52
12
  .clay-menu-edit::before { content: ""; position: absolute; inset: -8px 0; }
53
13
  .clay-menu-track { position: relative; display: block; box-sizing: border-box; width: 36px; height: 20px; border: 1px solid var(--bevel-line-2); background: var(--bevel-sunk); transition: background-color .14s, border-color .14s; }
54
14
  .clay-menu-thumb { position: absolute; top: 2px; left: 2px; box-sizing: border-box; width: 14px; height: 14px; border: 2px solid; border-color: var(--bevel-knob-hi) var(--bevel-edge-lo) var(--bevel-edge-lo) var(--bevel-knob-hi); background: var(--bevel-knob); transition: translate .15s cubic-bezier(.4, 0, .2, 1); }
55
- .clay-menu-edit:hover .clay-menu-track { border-color: var(--bevel-faint); }
56
- .clay-menu-edit[aria-checked="true"] .clay-menu-track { background: #256056; border-color: light-dark(#256056, #3C7E72); }
57
- .clay-menu-edit[aria-checked="true"] .clay-menu-thumb { translate: 16px 0; background: light-dark(var(--bevel-knob), #8EDCC5); border-color: light-dark(var(--bevel-knob-hi), #C2F0E2) light-dark(var(--bevel-edge-lo), #4F9C86) light-dark(var(--bevel-edge-lo), #4F9C86) light-dark(var(--bevel-knob-hi), #C2F0E2); }
15
+ :is(.clay-menu-edit, .clay-menu-toggle):hover .clay-menu-track { border-color: var(--bevel-faint); }
16
+ :is(.clay-menu-edit, .clay-menu-toggle)[aria-checked="true"] .clay-menu-track { background: #256056; border-color: light-dark(#256056, #3C7E72); }
17
+ :is(.clay-menu-edit, .clay-menu-toggle)[aria-checked="true"] .clay-menu-thumb { translate: 16px 0; background: light-dark(var(--bevel-knob), #8EDCC5); border-color: light-dark(var(--bevel-knob-hi), #C2F0E2) light-dark(var(--bevel-edge-lo), #4F9C86) light-dark(var(--bevel-edge-lo), #4F9C86) light-dark(var(--bevel-knob-hi), #C2F0E2); }
58
18
  .clay-menu-edit[aria-busy="true"] { cursor: progress; color: var(--bevel-muted); }
59
19
  .clay-menu-edit[aria-busy="true"] .clay-menu-track { background: var(--bevel-line); border-color: var(--bevel-line-2); }
60
20
  .clay-menu-edit[aria-busy="true"] .clay-menu-thumb { translate: 8px 0; opacity: .7; }
@@ -62,15 +22,9 @@ export const MENU_BASE = `.clay-menu-dock, .clay-menu-spots, .clay-seo {
62
22
 
63
23
  .clay-menu-divider { width: 1px; height: 18px; margin: 0 2px; background: var(--bevel-line-2); }
64
24
 
65
- .clay-menu-goo { position: relative; display: inline-grid; place-items: center; box-sizing: border-box; margin: 0; padding: 0; cursor: pointer; }
66
- .clay-menu-goo svg { display: block; }
67
- .clay-menu-dot { position: absolute; top: -1px; right: -2px; width: 8px; height: 8px; border-radius: 50%; background: var(--bevel-brass); box-shadow: 0 0 0 2px var(--bevel-surface); }
68
-
69
- .clay-menu :focus-visible, .clay-roster:focus-visible { outline: 2px solid var(--bevel-brass); outline-offset: 2px; }
70
- .clay-menu-edit:focus-visible { outline: none; }
71
- .clay-menu-edit:focus-visible .clay-menu-track { outline: 2px solid var(--bevel-brass); outline-offset: 2px; }
25
+ `;
72
26
 
73
- /* The roster, beside the capsule, only on a page that opts in. Flat, like the capsule. */
27
+ const OWNER_2 = `/* The roster, beside the capsule, only on a page that opts in. Flat, like the capsule. */
74
28
  .clay-roster { box-sizing: border-box; display: inline-flex; align-items: center; gap: 8px; height: 34px; margin: 0; padding: 0 10px 0 5px; border: 1px solid var(--bevel-line-3); background: var(--bevel-surface); box-shadow: var(--clay-shadow); font: 400 12px/1 var(--bevel-font-mono); color: var(--bevel-muted); cursor: pointer; }
75
29
  .clay-roster:hover, .clay-roster[aria-expanded="true"] { background: var(--bevel-sunk); }
76
30
  .clay-roster-faces { display: inline-flex; }
@@ -87,29 +41,9 @@ export const MENU_BASE = `.clay-menu-dock, .clay-menu-spots, .clay-seo {
87
41
  .clay-avatar[data-tone="brass"] { background: var(--bevel-brass-soft); color: var(--bevel-brass); border-color: color-mix(in srgb, var(--bevel-brass) 35%, var(--bevel-brass-soft)); }
88
42
  .clay-avatar[data-tone="teal"] { background: var(--bevel-teal-soft); color: var(--bevel-teal); border-color: color-mix(in srgb, var(--bevel-teal) 35%, var(--bevel-teal-soft)); }
89
43
 
90
- /* Popovers: the menu, the people panel, the status note. Below the dock, above it in a bottom corner. */
91
- .clay-menu-list, .clay-people, .clay-menu-note { position: absolute; z-index: 1; top: calc(100% + 8px); right: 0; box-sizing: border-box; margin: 0; color: var(--bevel-ink); text-align: left; }
92
- .clay-people, .clay-menu-note { border: 1px solid var(--bevel-line-2); background: var(--bevel-surface); box-shadow: var(--clay-shadow); }
93
-
94
- .clay-menu-list { width: max-content; max-width: calc(100vw - 24px); max-height: calc(100dvh - 72px); overflow-y: auto; overscroll-behavior: contain; }
95
- .clay-menu-list[popover] { position: fixed; inset: auto; margin: 0; }
96
- .clay-menu-list [role="menuitem"] { box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; margin: 0; border: 0; background: none; text-align: left; text-decoration: none; cursor: pointer; }
97
- .clay-menu-list [role="menuitem"]:hover:not([aria-disabled="true"]), .clay-menu-list [role="menuitem"]:focus-visible { outline: none; background: var(--bevel-sunk); }
98
- .clay-menu-list [role="menuitem"][aria-disabled="true"] { cursor: default; }
99
- .clay-menu-pair { display: flex; gap: 12px; padding: 3px 9px; }
100
- .clay-menu-list .clay-menu-pair [role="menuitem"] { flex: none; width: auto; justify-content: flex-start; gap: 9px; }
101
- .clay-menu-list kbd { flex: none; white-space: nowrap; }
102
- .clay-menu-row { display: flex; align-items: center; gap: 2px; }
103
- .clay-menu-list .clay-menu-row > [part~="item"] { flex: 1; width: auto; min-width: 0; }
104
- .clay-menu-list .clay-menu-row > [part~="dismiss"] { flex: none; width: 28px; justify-content: center; padding: 0; }
105
- .clay-menu-row > [part~="dismiss"] svg { display: block; width: 10px; height: 10px; fill: currentColor; }
106
- .clay-menu-upgrade { display: inline-flex; align-items: center; gap: 8px; }
107
- .clay-menu-upgrade::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--bevel-brass); }
108
- .clay-menu-version { margin: 0; }
109
-
110
- .clay-menu-note { z-index: 2; width: max-content; max-width: 264px; padding: 8px 12px; font-size: 13px; line-height: 1.45; }
44
+ `;
111
45
 
112
- /* The people panel: no lines between people, one line between sections. */
46
+ const OWNER_3 = `/* The people panel: no lines between people, one line between sections. */
113
47
  .clay-people { width: 300px; max-width: calc(100vw - 24px); padding: 0; }
114
48
  .clay-people-head { display: flex; align-items: center; gap: 10px; padding: 10px 8px 10px 14px; border-bottom: 1px solid var(--bevel-line); }
115
49
  .clay-people-title { flex: 1; margin: 0; font: 500 16px/1.2 var(--bevel-font-serif); }
@@ -143,13 +77,9 @@ export const MENU_BASE = `.clay-menu-dock, .clay-menu-spots, .clay-seo {
143
77
  .clay-menu-btn-primary:hover { background: color-mix(in srgb, var(--bevel-ink), var(--bevel-ground) 12%); }
144
78
  `;
145
79
 
146
- export const MENU_FLOATING = `/* The dock: the roster (when a page opts in) and the capsule, fixed in one corner. */
147
- .clay-menu-dock { position: fixed; z-index: 2147482000; top: max(12px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right)); }
148
- .clay-menu-dock[data-corner$="left"] { right: auto; left: max(12px, env(safe-area-inset-left)); flex-direction: row-reverse; }
149
- .clay-menu-dock[data-corner^="bottom"] { top: auto; bottom: max(12px, env(safe-area-inset-bottom)); }
150
- .clay-menu-dock[data-corner$="left"] :is(.clay-menu-list, .clay-people, .clay-menu-note) { right: auto; left: 0; }
151
- .clay-menu-dock[data-corner^="bottom"] :is(.clay-menu-list, .clay-people, .clay-menu-note) { top: auto; bottom: calc(100% + 8px); }
152
- .clay-menu-dock[data-dragging] .clay-menu { border-color: var(--bevel-brass); cursor: grabbing; }
80
+ export const MENU_BASE = SHELL_A + OWNER_1 + SHELL_B + OWNER_2 + SHELL_C + OWNER_3;
81
+
82
+ export const MENU_FLOATING = SHELL_FLOATING + `.clay-menu-dock[data-dragging] .clay-menu { border-color: var(--bevel-brass); cursor: grabbing; }
153
83
  .clay-menu-grip { position: relative; display: inline-grid; place-items: center; width: 18px; height: 28px; margin: 0; padding: 0; border: 0; background: none; color: var(--bevel-faint); cursor: grab; touch-action: none; }
154
84
  .clay-menu-grip::before { content: ""; position: absolute; inset: -8px -2px -8px -10px; }
155
85
  .clay-menu-grip:hover { color: var(--bevel-ink-2); }
@@ -165,35 +95,4 @@ export const MENU_FLOATING = `/* The dock: the roster (when a page opts in) and
165
95
  .clay-menu-spot[data-near] { border: 2px solid var(--bevel-brass); background: var(--bevel-brass-soft); }
166
96
  `;
167
97
 
168
- export const MENU_SKIN = `.clay-menu { height: 34px; padding: 0 4px 0 2px; border: 1px solid var(--bevel-line-3); background: var(--bevel-surface); box-shadow: var(--clay-shadow); }
169
- .clay-menu-goo { width: 28px; height: 28px; min-height: 0; border-radius: 0; border: 2px solid transparent; background: none; color: var(--bevel-ink); }
170
- .clay-menu-goo::before { content: ""; position: absolute; inset: -8px -8px -8px -4px; }
171
- .clay-menu-goo svg { width: 18px; height: auto; fill: currentColor; }
172
- .clay-menu-goo:hover svg { translate: 0 -1px; }
173
- .clay-menu-goo:active, .clay-menu-goo[aria-expanded="true"] { 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); }
174
- .clay-menu-goo:active svg, .clay-menu-goo[aria-expanded="true"] svg { translate: 1px 1px; }
175
- .clay-menu-list { min-width: 284px; padding: 6px; border: 1px solid var(--bevel-line-2); background: var(--bevel-surface); box-shadow: var(--clay-shadow); }
176
- .clay-menu-list [role="menuitem"] { min-height: 32px; padding: 5px 9px; font: 400 13px/1.35 var(--bevel-font-sans); color: var(--bevel-ink); }
177
- .clay-menu-list [role="menuitem"][aria-disabled="true"] { color: var(--bevel-faint); }
178
- .clay-menu-list [role="menuitem"][aria-disabled="true"] kbd { opacity: .6; }
179
- .clay-menu-list .clay-menu-pair [role="menuitem"] { min-height: 30px; padding: 4px 6px 4px 9px; border: 1px solid var(--bevel-line); }
180
- .clay-menu-list kbd { font: 500 11px/1 var(--bevel-font-mono); padding: 3px 5px; border: 1px solid var(--bevel-line-2); background: var(--bevel-sunk); color: var(--bevel-muted); }
181
- .clay-menu-list [part~="quiet"] { font-size: 12px; color: var(--bevel-muted); }
182
- .clay-menu-sep { height: 1px; margin: 4px 2px; background: var(--bevel-line); }
183
- .clay-menu-version { padding: 6px 9px 4px; font: 400 11.5px/1.4 var(--bevel-font-mono); color: var(--bevel-muted); }
184
- `;
185
-
186
- // Last, so no skin can shrink a touch target or bring motion back.
187
- export const MENU_TOUCH = `@media (pointer: coarse), (max-width: 680px) {
188
- .clay-menu-list [role="menuitem"] { min-height: 44px; font-size: 15px; }
189
- .clay-menu-list .clay-menu-pair [role="menuitem"] { min-height: 44px; }
190
- :is(.clay-menu-dock, .clay-seo) :is(input, textarea, select) { font-size: 16px; }
191
- .clay-seo-input, .clay-people-input { min-height: 40px; }
192
- .clay-menu-btn { min-height: 40px; }
193
- }
194
- @media (prefers-reduced-motion: reduce) {
195
- .clay-menu-track, .clay-menu-thumb { transition: none; }
196
- }
197
- `;
198
-
199
98
  export default MENU_BASE + MENU_FLOATING + MENU_SKIN + MENU_TOUCH;
@@ -5,8 +5,10 @@
5
5
  // only. Keep deletes every other look, after which the page is an ordinary one-look
6
6
  // document. A choice and Keep are undo entries and saves, like the SEO panel's Done.
7
7
  import { modeRefusalText } from "../../core/mode-refusal.js";
8
+ import { OFF, labelOf, looks, nameOf, paintLook, recordLook } from "./looks.js";
9
+
10
+ export { looks };
8
11
 
9
- const OFF = "not all";
10
12
  const RECEIPT = "variants-choose";
11
13
  const KEEP_ID = "clay.variants.keep";
12
14
  const CHOOSE_LABEL = "Change the page look";
@@ -19,10 +21,13 @@ const NEXT =
19
21
  '<svg viewBox="0 0 6 10" aria-hidden="true" focusable="false"><rect width="2" height="2"/><rect x="2" y="2" width="2" height="2"/>' +
20
22
  '<rect x="4" y="4" width="2" height="2"/><rect x="2" y="6" width="2" height="2"/><rect y="8" width="2" height="2"/></svg>';
21
23
 
22
- export const looks = () => [...document.querySelectorAll("head style[clay-variant]")];
23
- const nameOf = (style) => style.getAttribute("clay-variant");
24
24
  const names = () => looks().map(nameOf);
25
25
 
26
+ const labelFor = (name) => {
27
+ const style = looks().find((candidate) => nameOf(candidate) === name);
28
+ return style ? labelOf(style) : `Variant ${name}`;
29
+ };
30
+
26
31
  // The first block without media="not all" is the saved choice. A merge or a hand edit
27
32
  // can leave none or several; this tab paints the first one either way, and the next
28
33
  // choice writes a clean state.
@@ -52,17 +57,20 @@ async function afterBaseline() {
52
57
  }
53
58
 
54
59
  function joinNames(list) {
55
- const named = list.map((name) => `Variant ${name}`);
60
+ const named = list.map(labelFor);
56
61
  return named.length < 2 ? named.join("") : `${named.slice(0, -1).join(", ")} and ${named[named.length - 1]}`;
57
62
  }
58
63
 
59
64
  export function attachVariants({ root, capsule, addItem, removeItem, note, currentMode }) {
65
+ paintLook();
66
+
60
67
  let previewing = null;
61
68
  let pending = null;
62
69
  let copy = null;
63
70
  let pill = null;
64
71
  let drawn = null;
65
72
 
73
+ const wantsRow = Boolean(document.querySelector("[clay-menu-slot]")?.hasAttribute("clay-menu-looks"));
66
74
  const isEdit = () => currentMode() === "edit";
67
75
  const shown = () => previewing ?? savedName();
68
76
  const storage = () => window.clay && window.clay.storage && window.clay.storage.session;
@@ -102,6 +110,7 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
102
110
  if (!names().includes(name)) return { ok: false, code: "unknown-variant" };
103
111
  pending = previewing = null;
104
112
  writeHead(CHOOSE_LABEL, () => {
113
+ recordLook(name);
105
114
  for (const style of looks()) {
106
115
  if (nameOf(style) === name) style.removeAttribute("media");
107
116
  else if (style.getAttribute("media") !== OFF) style.setAttribute("media", OFF);
@@ -121,6 +130,9 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
121
130
  function keepNow(name) {
122
131
  pending = previewing = null;
123
132
  writeHead(KEEP_LABEL, () => {
133
+ // The kept block becomes an ordinary stylesheet, so `look` is what still names the
134
+ // theme: an upgrade brings the blocks back and paints this one from it.
135
+ recordLook(name);
124
136
  for (const style of looks()) {
125
137
  if (nameOf(style) === name) {
126
138
  style.removeAttribute("clay-variant");
@@ -139,7 +151,7 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
139
151
  const name = savedName();
140
152
  const others = names().filter((other) => other !== name);
141
153
  if (!others.length) return Promise.resolve({ ok: false, code: "unknown-variant" });
142
- const text = `Keep Variant ${name} and delete ${joinNames(others)}? The switcher goes away. Version history still has them.`;
154
+ const text = `Keep ${labelFor(name)} and delete ${joinNames(others)}? The switcher goes away. Version history still has them.`;
143
155
  return import("../../ui/dialogs.js")
144
156
  .then((m) => m.consent(text))
145
157
  .then(
@@ -220,20 +232,46 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
220
232
  if (all.length < 2) {
221
233
  pill?.remove();
222
234
  pill = null;
235
+ removeItem("clay.look");
236
+ removeItem("clay.look.use");
223
237
  removeItem(KEEP_ID);
224
238
  return;
225
239
  }
240
+ if (root.hasAttribute("data-embedded")) {
241
+ const name = shown();
242
+ if (wantsRow) {
243
+ addItem({
244
+ id: "clay.look",
245
+ section: "start",
246
+ stepper: {
247
+ label: "Switch look",
248
+ value: `${labelFor(name)} ${all.indexOf(name) + 1}/${all.length}`,
249
+ prev: () => step(-1),
250
+ next: () => step(1),
251
+ prevLabel: "Previous look",
252
+ nextLabel: "Next look",
253
+ },
254
+ });
255
+ } else {
256
+ removeItem("clay.look");
257
+ }
258
+ if (!isEdit() && previewing !== null) addItem({ id: "clay.look.use", section: "start", label: "Use this look", run: useShown });
259
+ else removeItem("clay.look.use");
260
+ if (isEdit()) addItem({ id: KEEP_ID, label: `Keep ${labelFor(savedName())}, remove the others…`, run: () => keep() });
261
+ else removeItem(KEEP_ID);
262
+ return;
263
+ }
226
264
  if (!pill) pill = buildPill();
227
265
  if (!pill.isConnected) root.insertBefore(pill, root.querySelector(".clay-roster") ?? capsule);
228
266
  const name = shown();
229
267
  const label = pill.querySelector(".clay-variants-name");
230
- label.textContent = `Variant ${name} `;
268
+ label.textContent = `${labelFor(name)} `;
231
269
  const count = document.createElement("small");
232
270
  count.textContent = `(${all.indexOf(name) + 1}/${all.length})`;
233
271
  label.append(count);
234
272
  pill.querySelector(".clay-variants-use").hidden = isEdit() || previewing === null;
235
273
  pill.querySelector(".clay-variants-keep").hidden = !isEdit();
236
- if (isEdit()) addItem({ id: KEEP_ID, label: `Keep Variant ${savedName()}, remove the others…`, run: () => keep() });
274
+ if (isEdit()) addItem({ id: KEEP_ID, label: `Keep ${labelFor(savedName())}, remove the others…`, run: () => keep() });
237
275
  else removeItem(KEEP_ID);
238
276
  }
239
277
 
@@ -261,22 +299,30 @@ export function attachVariants({ root, capsule, addItem, removeItem, note, curre
261
299
  refresh();
262
300
  });
263
301
 
302
+ const followRecord = () => {
303
+ if (document.documentElement.hasAttribute("look") && names().length > 1) recordLook(savedName());
304
+ };
305
+
264
306
  // A merge can rebuild a look block, which clears its CSSOM flag, or bring a peer's
265
307
  // choice or Keep; painting again settles all three.
266
308
  document.addEventListener("clay:sync-applied", () => {
267
- if (current()) refresh();
309
+ if (!current()) return;
310
+ followRecord();
311
+ refresh();
268
312
  });
269
313
 
270
314
  // An undo or redo of a choice or of Keep brings the blocks back or moves the saved one.
271
315
  // Every other entry leaves the looks alone, so only a change redraws and announces.
272
316
  document.addEventListener("clay:undo-change", () => {
273
- if (current() && state() !== drawn) refresh();
317
+ if (!current() || state() === drawn) return;
318
+ followRecord();
319
+ refresh();
274
320
  });
275
321
 
276
322
  api = window.clay.variants = {
277
323
  list: () => {
278
324
  const active = savedName();
279
- return names().map((name) => ({ name, label: `Variant ${name}`, active: name === active }));
325
+ return names().map((name) => ({ name, label: labelFor(name), active: name === active }));
280
326
  },
281
327
  get active() {
282
328
  return savedName();
@@ -0,0 +1,78 @@
1
+ import { createShell } from "./shell.js";
2
+ import { attachDomItems } from "./dom-items.js";
3
+ import { MENU_SAFETY, SHELL_BASE, SHELL_FLOATING, MENU_SKIN, MENU_TOUCH } from "./shell-style.js";
4
+ import { activeLook, labelOf, looks, nameOf, paintLook } from "./looks.js";
5
+
6
+ function attachLooks({ addItem, removeItem, slot }) {
7
+ const announce = () => {
8
+ const name = activeLook() && nameOf(activeLook());
9
+ document.dispatchEvent(new CustomEvent("clay:variant-change", { detail: { active: name, shown: name } }));
10
+ };
11
+ const draw = () => {
12
+ const all = looks();
13
+ if (all.length < 2 || !slot?.hasAttribute("clay-menu-looks")) return removeItem("clay.look");
14
+ const current = activeLook();
15
+ const by = (delta) => () => choose(nameOf(all[(all.indexOf(activeLook()) + delta + all.length) % all.length]));
16
+ addItem({
17
+ id: "clay.look",
18
+ section: "start",
19
+ stepper: {
20
+ label: "Switch look",
21
+ value: `${labelOf(current)} ${all.indexOf(current) + 1}/${all.length}`,
22
+ prev: by(-1),
23
+ next: by(1),
24
+ prevLabel: "Previous look",
25
+ nextLabel: "Next look",
26
+ },
27
+ });
28
+ };
29
+ const choose = (name) => {
30
+ if (!looks().some((style) => nameOf(style) === name)) return { ok: false, code: "unknown-variant" };
31
+ document.documentElement.setAttribute("look", name);
32
+ paintLook(name);
33
+ window.clay?.save?.soon?.();
34
+ draw();
35
+ announce();
36
+ return { ok: true };
37
+ };
38
+ if (looks().length >= 2) {
39
+ window.clay.variants = {
40
+ list: () => looks().map((style) => ({ name: nameOf(style), label: labelOf(style), active: style === activeLook() })),
41
+ get active() {
42
+ return activeLook() && nameOf(activeLook());
43
+ },
44
+ get shown() {
45
+ return activeLook() && nameOf(activeLook());
46
+ },
47
+ preview: choose,
48
+ choose,
49
+ keep: () => Promise.resolve({ ok: false, code: "not-editing" }),
50
+ };
51
+ document.addEventListener("clay:drafts-restored", () => {
52
+ draw();
53
+ announce();
54
+ });
55
+ draw();
56
+ }
57
+ }
58
+
59
+ const START_OVER = "Start over? Your changes in this browser are deleted, and the page goes back to how its owner saved it.";
60
+
61
+ function startOver() {
62
+ return import("../../ui/dialogs.js")
63
+ .then((m) => m.consent(START_OVER))
64
+ .then(() => window.clay.visitorDrafts.reset(), () => false);
65
+ }
66
+
67
+ export function start() {
68
+ if (document.querySelector('clay-editor-ui[data-clay-ui="menu"]')) return;
69
+ const shell = createShell({ sections: [null] });
70
+ const { slot, embedded, skin, addItem, removeItem, open, close, note, goo } = shell;
71
+ shell.mount(embedded ? [MENU_SAFETY, SHELL_BASE, skin ?? MENU_SKIN, MENU_TOUCH] : [MENU_SAFETY, SHELL_BASE, SHELL_FLOATING, MENU_SKIN, MENU_TOUCH]);
72
+ attachLooks({ addItem, removeItem, slot });
73
+ addItem({ id: "clay.download", label: "Download a copy", run: () => window.clay.visitorDrafts.download() });
74
+ addItem({ id: "clay.start-over", label: "Start over", run: startOver });
75
+ attachDomItems(shell);
76
+ window.clay.menu = { addItem, removeItem, open, close, note, embedded, button: goo };
77
+ document.dispatchEvent(new CustomEvent("clay:menu-ready"));
78
+ }