efficient-easel 0.99.1 → 0.100.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 (110) hide show
  1. package/README.md +281 -36
  2. package/dist/autosave/index.d.ts +12 -6
  3. package/dist/blocks/index.d.ts +2 -2
  4. package/dist/canvas/index.d.ts +3 -3
  5. package/dist/canvas/index.js +1 -1
  6. package/dist/chrome/chrome.css +96 -29
  7. package/dist/chrome/index.d.ts +72 -19
  8. package/dist/chrome/index.js +17 -21
  9. package/dist/chunk-3EDSDPIP.js +1 -0
  10. package/dist/chunk-67GNT6PF.js +1 -0
  11. package/dist/{chunk-PI26YXYD.js → chunk-6EIVTKZZ.js} +21 -14
  12. package/dist/chunk-A5UK76FC.js +6 -0
  13. package/dist/chunk-AZT7V4UM.js +10 -0
  14. package/dist/chunk-DICUMYMG.js +12 -0
  15. package/dist/chunk-HDVD27VB.js +1 -0
  16. package/dist/chunk-JQM3V7XQ.js +1 -0
  17. package/dist/{chunk-TDA4HH54.js → chunk-JUQ6YJCG.js} +1 -1
  18. package/dist/chunk-LA54QLP5.js +1 -0
  19. package/dist/chunk-LGKQXSKO.js +16 -0
  20. package/dist/chunk-M6F6LS3H.js +1 -0
  21. package/dist/chunk-MH5GUZFQ.js +9 -0
  22. package/dist/chunk-P6O363EE.js +6 -0
  23. package/dist/chunk-PAELDYYS.js +1 -0
  24. package/dist/{chunk-JYPOOHII.js → chunk-QKVR3K73.js} +3 -3
  25. package/dist/chunk-SAJUCHOX.js +1 -0
  26. package/dist/chunk-TRVZ7C23.js +1 -0
  27. package/dist/chunk-V7GIIXFB.js +16 -0
  28. package/dist/{chunk-IBOFSS56.js → chunk-WZ7TGAAT.js} +1 -1
  29. package/dist/chunk-ZJEBF4NB.js +1 -0
  30. package/dist/copy/copy.css +388 -5
  31. package/dist/copy/index.d.ts +271 -8
  32. package/dist/copy/index.js +31 -10
  33. package/dist/data/index.d.ts +4 -4
  34. package/dist/drag/drag.css +10 -1
  35. package/dist/drag/index.d.ts +102 -11
  36. package/dist/drag/index.js +18 -17
  37. package/dist/{editor/editor.css → easel/easel.css} +16 -1
  38. package/dist/{editor → easel}/index.d.ts +146 -51
  39. package/dist/easel/index.js +9 -0
  40. package/dist/edit-lock/index.d.ts +3 -3
  41. package/dist/edit-lock/index.js +1 -1
  42. package/dist/fields/fields.css +103 -26
  43. package/dist/fields/index.d.ts +51 -13
  44. package/dist/fields/index.js +30 -46
  45. package/dist/gold/index.js +1 -1
  46. package/dist/icons/index.d.ts +10 -1
  47. package/dist/icons/index.js +1 -1
  48. package/dist/index.d.ts +3 -3
  49. package/dist/index.js +1 -1
  50. package/dist/markup/index.d.ts +94 -10
  51. package/dist/markup/index.js +1 -1
  52. package/dist/menu/index.d.ts +61 -6
  53. package/dist/menu/index.js +15 -15
  54. package/dist/menu/menu.css +17 -6
  55. package/dist/node-ops/index.d.ts +9 -9
  56. package/dist/node-ops/index.js +1 -1
  57. package/dist/panel/index.d.ts +73 -5
  58. package/dist/panel/index.js +18 -16
  59. package/dist/panel/panel.css +56 -1
  60. package/dist/pick/index.d.ts +42 -4
  61. package/dist/pick/index.js +7 -14
  62. package/dist/pick/pick.css +14 -0
  63. package/dist/rail/index.d.ts +6 -6
  64. package/dist/rail/index.js +1 -1
  65. package/dist/rail/rail.css +1 -1
  66. package/dist/strings/catalogue.d.ts +24 -40
  67. package/dist/strings/catalogue.js +1 -1
  68. package/dist/strings/index.d.ts +25 -23
  69. package/dist/strings/index.js +2 -2
  70. package/dist/style-clipboard/index.d.ts +5 -5
  71. package/dist/style-clipboard/index.js +1 -1
  72. package/dist/text-styles/index.d.ts +328 -0
  73. package/dist/text-styles/index.js +38 -0
  74. package/dist/text-styles/text-styles.css +309 -0
  75. package/dist/tree/index.d.ts +44 -3
  76. package/dist/tree/index.js +1 -1
  77. package/dist/tree/tree.css +71 -0
  78. package/dist/{types-CnRzp9oc.d.ts → types-DGcCsxs8.d.ts} +79 -12
  79. package/dist/{types-DNrn_w4V.d.ts → types-DWS-s9z-.d.ts} +10 -10
  80. package/dist/{types-pGBXevBU.d.ts → types-Djdq6Xkd.d.ts} +15 -8
  81. package/dist/{types-Dy6oR6Z2.d.ts → types-E3HrTPg2.d.ts} +20 -14
  82. package/dist/{types-C7HFvdtk.d.ts → types-VqL6t5HH.d.ts} +82 -5
  83. package/dist/{watch-DmasQA61.d.ts → watch-BTYOJpQU.d.ts} +19 -2
  84. package/dist/{wordFit-9B9nqyf3.d.ts → wordFit-D4S36PvB.d.ts} +1 -1
  85. package/dist/{words-DagJdwIA.d.ts → words-AHHfiaRY.d.ts} +31 -2
  86. package/dist/{words-D9ChvbNC.d.ts → words-BDignMl_.d.ts} +1 -1
  87. package/dist/{words-Cqmyey0s.d.ts → words-BMzV04b1.d.ts} +2 -2
  88. package/dist/words-CJjUKcV5.d.ts +37 -0
  89. package/dist/words-CRYvAiHX.d.ts +105 -0
  90. package/dist/{words-B2YLaKEr.d.ts → words-CnnHb9jo.d.ts} +1 -1
  91. package/dist/{words-CD3WADyb.d.ts → words-CzoyDNHm.d.ts} +4 -0
  92. package/dist/{words-bOMsUJk9.d.ts → words-DBq7vfHy.d.ts} +59 -6
  93. package/dist/{words-DOhF-4D4.d.ts → words-DMG4dYq2.d.ts} +1 -1
  94. package/dist/{words-BK-npta-.d.ts → words-DgHcY5qB.d.ts} +10 -4
  95. package/dist/{words-B87GIFWH.d.ts → words-is_Vy6hn.d.ts} +1 -1
  96. package/package.json +14 -8
  97. package/dist/chunk-66CKSTXP.js +0 -14
  98. package/dist/chunk-67VZRQLG.js +0 -13
  99. package/dist/chunk-6PS6STGA.js +0 -1
  100. package/dist/chunk-7A7O2JWQ.js +0 -1
  101. package/dist/chunk-H4B72XCJ.js +0 -1
  102. package/dist/chunk-JE63JHKO.js +0 -1
  103. package/dist/chunk-KDIVA6WD.js +0 -1
  104. package/dist/chunk-MCLBHENY.js +0 -1
  105. package/dist/chunk-QB3BZIRA.js +0 -5
  106. package/dist/chunk-SJQG5ENJ.js +0 -1
  107. package/dist/chunk-SUO5627P.js +0 -1
  108. package/dist/chunk-ZT7M24XD.js +0 -1
  109. package/dist/editor/index.js +0 -9
  110. /package/dist/{chunk-GGTPNGFJ.js → chunk-AELNNAFM.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { a as EditorContextMenuProps, b as EditorContextMenuSkin } from '../types-pGBXevBU.js';
3
- export { E as EditorContextMenuItem } from '../types-pGBXevBU.js';
2
+ import { a as EaselContextMenuProps, b as EaselContextMenuSkin } from '../types-Djdq6Xkd.js';
3
+ export { E as EaselContextMenuItem } from '../types-Djdq6Xkd.js';
4
4
 
5
5
  /**
6
6
  * The right-click menu box, shared by both editors (card 66400.d).
@@ -17,7 +17,7 @@ export { E as EditorContextMenuItem } from '../types-pGBXevBU.js';
17
17
  * the box no longer knows what it looks like. The admin version hard-coded ONE
18
18
  * editor's surface — `glassStyle()` from efficient-shell, plus that portal's
19
19
  * Tailwind palette — into the only box both editors were ever going to share.
20
- * Here the look is a {@link EditorContextMenuSkin}, the default is the
20
+ * Here the look is a {@link EaselContextMenuSkin}, the default is the
21
21
  * storefront's approved one, and the admin passes its own strings in. So this
22
22
  * module imports nothing but React (`tests/menu-leaf-safety.test.ts`), and the
23
23
  * admin's rendered markup is byte-identical to what shipped
@@ -28,7 +28,62 @@ export { E as EditorContextMenuItem } from '../types-pGBXevBU.js';
28
28
  * server component may import the module and must render it inside the host's
29
29
  * client boundary.
30
30
  */
31
- declare function EditorContextMenu({ x, y, items, onClose, maxHeight, skin, portalTo, }: EditorContextMenuProps): react.ReactPortal | null;
31
+ declare function EaselContextMenu({ x, y, items, onClose, maxHeight, skin, portalTo, }: EaselContextMenuProps): react.ReactPortal | null;
32
+
33
+ /**
34
+ * WHERE THE MENU BOX LIVES — the decision, and the one check behind it
35
+ * (second host, 2026-10-07).
36
+ *
37
+ * The box is portalled to `<body>`. A host's axe-core run flags that under
38
+ * `region` (a best-practice rule, moderate): a `role="menu"` hanging off the
39
+ * body is page content outside every landmark. Two answers were on the table.
40
+ *
41
+ * A. Wrap the box in something a landmark check lets through — a
42
+ * `role="region"`, a `role="dialog"`. REFUSED.
43
+ * A menu is neither. A region is a landmark, and one that appears in the
44
+ * page's landmark list each time a menu opens is noise for the very
45
+ * people landmarks are for; a dialog is announced as one and is expected
46
+ * to hold the focus like one. Changing what a thing IS so a checker
47
+ * stops reading it is the wrong way round.
48
+ *
49
+ * B. Portal the box INTO the editor's root, which sits inside whatever
50
+ * landmark the host put its editor in. TAKEN,
51
+ * as the host's choice (`portalTo`), with this check.
52
+ *
53
+ * WHY IT IS NOT THE DEFAULT. The box is `position: fixed` and placed at the
54
+ * pointer in the VIEWPORT's coordinates. An ancestor that is transformed,
55
+ * filtered or contained becomes the containing block of a fixed box inside it:
56
+ * the box is then placed against that ancestor and clipped by it — the menu
57
+ * opens somewhere the pointer is not, or is cut off. `<body>` is the one place
58
+ * that can never happen, the box does not know where a host's root is, and
59
+ * every surface that draws it today ships on the body. So the body stays the
60
+ * default, and a host that wants the box in its landmarks says where:
61
+ *
62
+ * <EaselContextMenu portalTo={menuPortalTarget(rootRef.current)} … />
63
+ *
64
+ * `menuPortalTarget` hands the root back only when a fixed box in it is still
65
+ * placed against the viewport, and `null` — the body — otherwise. So a root
66
+ * that is scaled on one screen and not on another gets the right answer on
67
+ * both, with no rule of the host's own.
68
+ *
69
+ * Two things the host still owns: the root's stacking (the box is `z-index:
70
+ * 9999` INSIDE the root's stacking context, so an overlay the host draws above
71
+ * its editor is above the menu too), and its tokens (the default skin names
72
+ * `es-editor` on the box, which re-declares the token defaults there — a
73
+ * re-themed root passes a skin without it; see `ES_MENU_SKIN`).
74
+ *
75
+ * Pure DOM, no React, nothing read at import.
76
+ */
77
+ /**
78
+ * The element to hand `EaselContextMenu` as `portalTo` so the box lives inside
79
+ * the editor: `root` itself when a fixed box in it is still placed against the
80
+ * viewport, `null` (the body, the default) when the root or anything around it
81
+ * would place and clip the box instead — or when there is no root yet.
82
+ *
83
+ * Call it when the menu opens, not once: whether a root is transformed can
84
+ * change with the screen (a host that scales its editor to fit).
85
+ */
86
+ declare function menuPortalTarget(root: Element | null | undefined): HTMLElement | null;
32
87
 
33
88
  /**
34
89
  * The default skin — the storefront editor's approved look, in classes that
@@ -44,6 +99,6 @@ declare function EditorContextMenu({ x, y, items, onClose, maxHeight, skin, port
44
99
  * present, answers every query a test can ask, and cannot be seen. Pinned by
45
100
  * `tests/menu-layer.test.tsx` (f).
46
101
  */
47
- declare const ES_MENU_SKIN: EditorContextMenuSkin;
102
+ declare const ES_MENU_SKIN: EaselContextMenuSkin;
48
103
 
49
- export { ES_MENU_SKIN, EditorContextMenu, EditorContextMenuProps, EditorContextMenuSkin };
104
+ export { ES_MENU_SKIN, EaselContextMenu, EaselContextMenuProps, EaselContextMenuSkin, menuPortalTarget };
@@ -1,15 +1,15 @@
1
- import{useRef as e,useState as n,useLayoutEffect as t,useEffect as l}from"react";import{createPortal as r}from"react-dom";import{jsxs as i,Fragment as a,jsx as o}from"react/jsx-runtime";var c={menu:"es-editor es-menu",menuSub:"es-editor es-menu",item:"es-menu-item",itemDanger:"es-menu-item is-danger",divider:"es-menu-divider",header:"es-menu-header",icon:"es-menu-icon",shortcut:"es-menu-shortcut",label:"es-menu-label",labelSub:"es-menu-label es-menu-label-sub",chevron:"es-menu-chevron",tick:"es-menu-tick"};function u({x:u,y:s,items:d,onClose:m,maxHeight:h,skin:b,portalTo:y}){const p=e(null),g=e(null),[f,w]=n(null),[v,x]=n({x:u,y:s}),[N,k]=n({x:u,y:s});if(N.x===u&&N.y===s||(k({x:u,y:s}),x({x:u,y:s})),t(()=>{const e=p.current;if(!e)return;const{width:n,height:t}=e.getBoundingClientRect(),l=window.innerWidth,r=window.innerHeight;let i=s;s+t>r-8&&(i=s-t>=8?s-t:Math.max(8,r-t-8));let a=u;u+n>l-8&&(a=u-n>=8?u-n:Math.max(8,l-n-8)),a===v.x&&i===v.y||x({x:a,y:i})},[u,s,d,v.x,v.y]),l(()=>{const e=document.activeElement;return p.current?.querySelector('[role="menuitem"]')?.focus(),()=>e?.focus?.()},[]),l(()=>{const e=e=>{const n=e.target;g.current?.contains(n)||p.current&&!p.current.contains(n)&&m()},n=e=>{"Escape"===e.key&&m()},t=setTimeout(()=>{window.addEventListener("mousedown",e),window.addEventListener("contextmenu",e),window.addEventListener("keydown",n)},0);return()=>{clearTimeout(t),window.removeEventListener("mousedown",e),window.removeEventListener("contextmenu",e),window.removeEventListener("keydown",n)}},[m]),0===d.length)return null;const C=b??c,E=e=>e.danger?C.itemDanger:C.item,S=({on:e})=>/* @__PURE__ */o("span",{className:C.tick,"aria-hidden":"true",children:e?"✓":""}),L=({of:e})=>null==e.icon?null:/* @__PURE__ */o("span",{className:C.icon,"aria-hidden":"true",children:e.icon}),T=({of:e})=>null==e.shortcut?null:/* @__PURE__ */o("span",{className:C.shortcut,"aria-hidden":"true",children:e.shortcut}),B=e=>null!=e.icon||null!=e.shortcut,M=(e,n,t)=>{if(!e.children?.length)return void w(null);const l=t.getBoundingClientRect(),r=p.current,i=r?parseFloat(getComputedStyle(r).minWidth):NaN,a=Number.isFinite(i)&&i>0?i:r?.getBoundingClientRect().width??0,o=l.right+a>window.innerWidth-8?l.left-a:l.right;w({i:n,x:Math.max(8,o),y:l.top})},F=f?d[f.i]:null,H=e=>({...C.surface,...e});return r(
2
- /* @__PURE__ */i(a,{children:[
3
- /* @__PURE__ */o("div",{ref:p,"data-es-menu":"",role:"menu","aria-label":d.find(e=>e.header)?.label,className:C.menu,style:H({left:v.x,top:v.y,maxHeight:h||void 0}),children:d.map((e,n)=>"—"===e.label?
4
- /* @__PURE__ */o("div",{role:"separator",className:C.divider},n):e.header?/* @__PURE__ */o("div",{className:C.header,children:e.label},n):e.children?.length?/* @__PURE__ */i("button",{type:"button",role:"menuitem","aria-haspopup":"menu","aria-expanded":f?.i===n,onMouseEnter:t=>M(e,n,t.currentTarget),onFocus:t=>M(e,n,t.currentTarget),onClick:t=>M(e,n,t.currentTarget),className:E(e),style:C.itemStyle,children:[
5
- /* @__PURE__ */o(L,{of:e}),
6
- /* @__PURE__ */o("span",{className:C.label,children:e.label}),
7
- /* @__PURE__ */o(T,{of:e}),
8
- /* @__PURE__ */o("span",{"aria-hidden":"true",className:C.chevron,children:"›"})]},n):/* @__PURE__ */o("button",{type:"button",role:"menuitem",onMouseEnter:()=>w(null),onClick:()=>{e.onClick(),m()},className:E(e),style:C.itemStyle,children:B(e)?/* @__PURE__ */i(a,{children:[
9
- /* @__PURE__ */o(L,{of:e}),
10
- /* @__PURE__ */o("span",{className:C.label,children:e.label}),
11
- /* @__PURE__ */o(T,{of:e})]}):e.label},n))}),F?.children?.length&&f?/* @__PURE__ */o("div",{ref:g,"data-es-menu":"",role:"menu","aria-label":F.label,className:C.menuSub??C.menu,style:H({left:f.x,top:f.y}),children:F.children.map((e,n)=>/* @__PURE__ */i("button",{type:"button",role:"menuitemradio","aria-checked":Boolean(e.checked),onClick:()=>{e.onClick(),m()},className:E(e),style:C.itemStyle,children:[
12
- /* @__PURE__ */o(S,{on:Boolean(e.checked)}),
13
- /* @__PURE__ */o(L,{of:e}),
14
- /* @__PURE__ */o("span",{className:C.labelSub??C.label,children:e.label}),
15
- /* @__PURE__ */o(T,{of:e})]},n))}):null]}),y??document.body)}export{c as ES_MENU_SKIN,u as EditorContextMenu};
1
+ import{useRef as e,useState as n,useLayoutEffect as t,useEffect as r}from"react";import{createPortal as l}from"react-dom";import{jsxs as i,Fragment as o,jsx as a}from"react/jsx-runtime";var c={menu:"es-editor es-menu",menuSub:"es-editor es-menu",item:"es-menu-item",itemDanger:"es-menu-item is-danger",divider:"es-menu-divider",header:"es-menu-header",icon:"es-menu-icon",shortcut:"es-menu-shortcut",label:"es-menu-label",labelSub:"es-menu-label es-menu-label-sub",chevron:"es-menu-chevron",tick:"es-menu-tick"};function s({x:s,y:u,items:d,onClose:m,maxHeight:h,skin:b,portalTo:p}){const f=e(null),y=e(null),[g,v]=n(null),[w,x]=n({x:s,y:u}),[k,N]=n({x:s,y:u});if(k.x===s&&k.y===u||(N({x:s,y:u}),x({x:s,y:u})),t(()=>{const e=f.current;if(!e)return;const{width:n,height:t}=e.getBoundingClientRect(),r=window.innerWidth,l=window.innerHeight;let i=u;u+t>l-8&&(i=u-t>=8?u-t:Math.max(8,l-t-8));let o=s;s+n>r-8&&(o=s-n>=8?s-n:Math.max(8,r-n-8)),o===w.x&&i===w.y||x({x:o,y:i})},[s,u,d,w.x,w.y]),r(()=>{const e=document.activeElement;return f.current?.querySelector('[role="menuitem"]')?.focus(),()=>e?.focus?.()},[]),r(()=>{const e=e=>{const n=e.target;y.current?.contains(n)||f.current&&!f.current.contains(n)&&m()},n=e=>{"Escape"===e.key&&m()},t=setTimeout(()=>{window.addEventListener("mousedown",e),window.addEventListener("contextmenu",e),window.addEventListener("keydown",n)},0);return()=>{clearTimeout(t),window.removeEventListener("mousedown",e),window.removeEventListener("contextmenu",e),window.removeEventListener("keydown",n)}},[m]),0===d.length)return null;const C=b??c,E=e=>e.danger?C.itemDanger:C.item,S=({on:e})=>/* @__PURE__ */a("span",{className:C.tick,"aria-hidden":"true",children:e?"✓":""}),L=({of:e})=>null==e.icon?null:/* @__PURE__ */a("span",{className:C.icon,"aria-hidden":"true",children:e.icon}),T=({of:e})=>null==e.shortcut?null:/* @__PURE__ */a("span",{className:C.shortcut,"aria-hidden":"true",children:e.shortcut}),B=e=>null!=e.icon||null!=e.shortcut,M=(e,n,t)=>{if(!e.children?.length)return void v(null);const r=t.getBoundingClientRect(),l=f.current,i=l?parseFloat(getComputedStyle(l).minWidth):NaN,o=Number.isFinite(i)&&i>0?i:l?.getBoundingClientRect().width??0,a=r.right+o>window.innerWidth-8?r.left-o:r.right;v({i:n,x:Math.max(8,a),y:r.top})},D=g?d[g.i]:null,F=e=>({...C.surface,...e});return l(
2
+ /* @__PURE__ */i(o,{children:[
3
+ /* @__PURE__ */a("div",{ref:f,"data-es-menu":"",role:"menu","aria-label":d.find(e=>e.header)?.label,className:C.menu,style:F({left:w.x,top:w.y,maxHeight:h||void 0}),children:d.map((e,n)=>"—"===e.label?
4
+ /* @__PURE__ */a("div",{role:"separator",className:C.divider},n):e.header?/* @__PURE__ */a("div",{className:C.header,children:e.label},n):e.children?.length?/* @__PURE__ */i("button",{type:"button",role:"menuitem","aria-haspopup":"menu","aria-expanded":g?.i===n,onMouseEnter:t=>M(e,n,t.currentTarget),onFocus:t=>M(e,n,t.currentTarget),onClick:t=>M(e,n,t.currentTarget),className:E(e),style:C.itemStyle,children:[
5
+ /* @__PURE__ */a(L,{of:e}),
6
+ /* @__PURE__ */a("span",{className:C.label,children:e.label}),
7
+ /* @__PURE__ */a(T,{of:e}),
8
+ /* @__PURE__ */a("span",{"aria-hidden":"true",className:C.chevron,children:"›"})]},n):/* @__PURE__ */a("button",{type:"button",role:"menuitem",onMouseEnter:()=>v(null),onClick:()=>{e.onClick(),m()},className:E(e),style:C.itemStyle,children:B(e)?/* @__PURE__ */i(o,{children:[
9
+ /* @__PURE__ */a(L,{of:e}),
10
+ /* @__PURE__ */a("span",{className:C.label,children:e.label}),
11
+ /* @__PURE__ */a(T,{of:e})]}):e.label},n))}),D?.children?.length&&g?/* @__PURE__ */a("div",{ref:y,"data-es-menu":"",role:"menu","aria-label":D.label,className:C.menuSub??C.menu,style:F({left:g.x,top:g.y}),children:D.children.map((e,n)=>/* @__PURE__ */i("button",{type:"button",role:"menuitemradio","aria-checked":Boolean(e.checked),onClick:()=>{e.onClick(),m()},className:E(e),style:C.itemStyle,children:[
12
+ /* @__PURE__ */a(S,{on:Boolean(e.checked)}),
13
+ /* @__PURE__ */a(L,{of:e}),
14
+ /* @__PURE__ */a("span",{className:C.labelSub??C.label,children:e.label}),
15
+ /* @__PURE__ */a(T,{of:e})]},n))}):null]}),p??document.body)}var u=/\b(transform|translate|rotate|scale|perspective|filter|backdrop-filter|contain)\b/;function d(e){const n=n=>e.getPropertyValue(n).trim(),t=(e,t)=>{const r=n(e);return""!==r&&!t.includes(r)};return!!t("transform",["none"])||(!!(t("translate",["none"])||t("rotate",["none"])||t("scale",["none"]))||(!!t("perspective",["none"])||(!!t("filter",["none"])||(!(!t("backdrop-filter",["none"])&&!t("-webkit-backdrop-filter",["none"]))||(!!/\b(layout|paint|strict|content)\b/.test(n("contain"))||(!!t("container-type",["normal"])||(!!t("content-visibility",["visible"])||u.test(n("will-change")))))))))}function m(e){const n=e?.ownerDocument,t=n?.defaultView;if(!(e&&n&&t&&e.isConnected))return null;for(let r=e;r&&r!==n.body&&r!==n.documentElement;r=r.parentElement)if(d(t.getComputedStyle(r)))return null;return e}export{c as ES_MENU_SKIN,s as EaselContextMenu,m as menuPortalTarget};
@@ -115,12 +115,16 @@
115
115
  than copied as a literal. */
116
116
  --es-menu-danger-fg: var(--es-danger-strong);
117
117
 
118
- /* THE SHORTCUT COLUMN'S INK — quieter than a label, quieter than `--es-muted`,
119
- because a shortcut is a reminder and never the thing being read.
120
- ⚠️ The one value here with no shell token behind it: the drawing's own
121
- `#b6bcc4`, which is lighter than `--es-disabled` (#9ca3af). Named here
122
- rather than reaching for a token that is not this colour. */
123
- --es-menu-shortcut-fg: #b6bcc4;
118
+ /* THE SHORTCUT COLUMN'S INK — quieter than a label, because a shortcut is a
119
+ reminder and never the thing being read; but it IS read, so it is a grey
120
+ that can be: `--es-muted`, 4.8:1 on the box at 11px.
121
+ ⚠️ It was the drawing's own `#b6bcc4` until 2026-10-07 — lighter than
122
+ `--es-disabled`, 1.9:1 on white, the lowest contrast of any text in the
123
+ package. A second host's axe-core run failed it (WCAG AA, 4.5:1) and the
124
+ host put `--es-muted` here itself; this is that, at the source. Still
125
+ behind ONE var, so the design can pick another grey that can be read.
126
+ Held by ratio in tests/contrast-small-text.test.ts. */
127
+ --es-menu-shortcut-fg: var(--es-muted);
124
128
  }
125
129
 
126
130
 
@@ -225,6 +229,13 @@
225
229
  color: var(--es-menu-shortcut-fg);
226
230
  font-size: 11px;
227
231
  }
232
+ /* On the row that is pointed at the ground is tinted, and the grey that passes
233
+ on white does not pass on the tint (4.4:1). So the shortcut takes the ROW's
234
+ ink there — the accent on a plain row (4.7:1), the red on one that destroys
235
+ (5.9:1): one ink per row, and no new colour. */
236
+ .es-menu-item:hover .es-menu-shortcut {
237
+ color: inherit;
238
+ }
228
239
 
229
240
  /* Present on every row of a flyout that uses one, so the labels line up whether
230
241
  or not the row is the state you are already on. */
@@ -1,8 +1,8 @@
1
1
  export { A as AfterRemove, I as ItemSelector, N as NodeSelection, P as Planned, S as StepBounds, c as canStep, f as fromItemSelector, p as parentStep, a as planDuplicate, b as planMove, d as planPaste, e as planRemove, g as planSetProps, h as planStep, t as toItemSelector } from '../plan-DChbUXdS.js';
2
2
  export { C as ClipboardEntry, N as NodeClipboard, a as NodeOps, b as NodeOpsHost, c as createNodeClipboard, u as useNodeOps } from '../useNodeOps-BF0fQsif.js';
3
3
  import { ReactNode } from 'react';
4
- import { E as EditorContextMenuItem } from '../types-pGBXevBU.js';
5
- import { N as NodeMenuWords } from '../words-DOhF-4D4.js';
4
+ import { E as EaselContextMenuItem } from '../types-Djdq6Xkd.js';
5
+ import { N as NodeMenuWords } from '../words-DMG4dYq2.js';
6
6
  import '../types-41704JE4.js';
7
7
 
8
8
  /**
@@ -10,7 +10,7 @@ import '../types-41704JE4.js';
10
10
  *
11
11
  * The campaign designer (`blockMenuItems`) and the storefront editor
12
12
  * (`nodeMenuItems`) each built their own, in the same row type and drawn by the
13
- * same `EditorContextMenu`, and they had drifted: different group order, the
13
+ * same `EaselContextMenu`, and they had drifted: different group order, the
14
14
  * layer flyout in a different group, Copy / Cut / Paste / Rename on one side
15
15
  * only, and no pinned-block rule on the storefront. The ORDER and the GROUPS
16
16
  * are decided here once:
@@ -34,7 +34,7 @@ import '../types-41704JE4.js';
34
34
  */
35
35
 
36
36
  type NodeMenuRowId = 'layer' | 'addInside' | 'duplicate' | 'moveUp' | 'moveDown' | 'copy' | 'cut' | 'paste' | 'rename' | 'hide' | 'show' | 'upLevel' | 'delete';
37
- type RowLook = Partial<Pick<EditorContextMenuItem, 'label' | 'icon' | 'shortcut' | 'danger'>> & {
37
+ type RowLook = Partial<Pick<EaselContextMenuItem, 'label' | 'icon' | 'shortcut' | 'danger'>> & {
38
38
  /** `null` draws the row with no glyph. */
39
39
  icon?: ReactNode | null;
40
40
  };
@@ -43,7 +43,7 @@ interface NodeMenuOptions {
43
43
  title: string;
44
44
  /** The layer flyout: its entries (built by the host from the ancestor chain) and which group it sits in. */
45
45
  layer?: {
46
- items: EditorContextMenuItem[];
46
+ items: EaselContextMenuItem[];
47
47
  group?: 'pick' | 'change';
48
48
  };
49
49
  /** Each present action is offered; an absent one is not. */
@@ -69,7 +69,7 @@ interface NodeMenuOptions {
69
69
  delete?: () => void;
70
70
  };
71
71
  /** The host's style rows (styleMenuItems). Offered on pinned blocks too. */
72
- style?: EditorContextMenuItem[];
72
+ style?: EaselContextMenuItem[];
73
73
  /**
74
74
  * The block cannot move or be removed: show this line when nothing else is on
75
75
  * offer. It is a CAPTION, like the title — it says why the menu is short, and
@@ -79,13 +79,13 @@ interface NodeMenuOptions {
79
79
  /** Per-row look overrides: label, icon (`null` = none), shortcut, danger. */
80
80
  rows?: Partial<Record<NodeMenuRowId, RowLook>>;
81
81
  /** Host rows added to the end of a group. */
82
- extras?: Partial<Record<'pick' | 'make' | 'change' | 'remove', EditorContextMenuItem[]>>;
82
+ extras?: Partial<Record<'pick' | 'make' | 'change' | 'remove', EaselContextMenuItem[]>>;
83
83
  /**
84
- * The rows' own words in the host's language: `useEditorStrings().menu`
84
+ * The rows' own words in the host's language: `useEaselStrings().menu`
85
85
  * (0.95.0). Left out, English. A row's `rows` override still wins.
86
86
  */
87
87
  words?: NodeMenuWords;
88
88
  }
89
- declare function nodeMenuItems(options: NodeMenuOptions): EditorContextMenuItem[];
89
+ declare function nodeMenuItems(options: NodeMenuOptions): EaselContextMenuItem[];
90
90
 
91
91
  export { type NodeMenuOptions, type NodeMenuRowId, NodeMenuWords, nodeMenuItems };
@@ -1 +1 @@
1
- import{DEFAULT_NODE_MENU_WORDS as e}from"../chunk-WK3AFPXY.js";export{canStep,fromItemSelector,parentStep,planDuplicate,planMove,planPaste,planRemove,planSetProps,planStep,toItemSelector,useNodeOps}from"../chunk-D2BCHTQR.js";import{TrashIcon as n,EyeIcon as t,EyeOffIcon as o,RenameIcon as p,PasteIcon as l,CopyIcon as c,MoveDownIcon as s,MoveUpIcon as a,DuplicateIcon as i,AddIcon as r,LayersIcon as u}from"../chunk-PI26YXYD.js";import"../chunk-KRXQM65X.js";import"../chunk-NKB3KVAB.js";import{jsx as h}from"react/jsx-runtime";function d(){let e=null;const n=/* @__PURE__ */new Set;return{get:()=>e,set(t){e=t;for(const e of[...n])e()},subscribe:e=>(n.add(e),()=>n.delete(e))}}var m={layer:{icon:/* @__PURE__ */h(u,{})},addInside:{icon:/* @__PURE__ */h(r,{})},duplicate:{icon:/* @__PURE__ */h(i,{})},moveUp:{icon:/* @__PURE__ */h(a,{})},moveDown:{icon:/* @__PURE__ */h(s,{})},copy:{icon:/* @__PURE__ */h(c,{}),shortcut:"⌘C"},cut:{icon:/* @__PURE__ */h(c,{}),shortcut:"⌘X"},paste:{icon:/* @__PURE__ */h(l,{}),shortcut:"⌘V"},rename:{icon:/* @__PURE__ */h(p,{})},hide:{icon:/* @__PURE__ */h(o,{})},show:{icon:/* @__PURE__ */h(t,{})},upLevel:{},delete:{icon:/* @__PURE__ */h(n,{}),danger:!0}};function v(n){const{actions:t,rows:o={},extras:p={},words:l=e}=n,c=e=>({...m[e],label:l[e],...o[e]}),s=(e,n,t)=>{const o=c(e),p={label:t??o.label??e,onClick:n};return o.icon&&(p.icon=o.icon),o.shortcut&&(p.shortcut=o.shortcut),o.danger&&(p.danger=!0),p},a=()=>{const e=c("layer"),t={label:e.label??l.layer,onClick:()=>{},children:n.layer.items};return e.icon&&(t.icon=e.icon),t},i=[],r=[],u=[],h=[],d=!!n.layer&&n.layer.items.length>1;if(d&&"pick"===(n.layer.group??"pick")&&i.push(a()),t.addInside&&r.push(s("addInside",t.addInside)),t.duplicate&&r.push(s("duplicate",t.duplicate)),t.moveUp&&r.push(s("moveUp",t.moveUp)),t.moveDown&&r.push(s("moveDown",t.moveDown)),t.copy&&r.push(s("copy",t.copy)),t.cut&&r.push(s("cut",t.cut)),t.paste){const e=c("paste").label??l.paste;r.push(s("paste",t.paste.run,l.pasteNamed({paste:e,label:t.paste.label})))}t.rename&&u.push(s("rename",t.rename)),t.hide&&u.push(t.hide.hidden?s("show",t.hide.run):s("hide",t.hide.run)),d&&"change"===n.layer.group&&u.push(a()),t.upLevel&&u.push(s("upLevel",t.upLevel)),t.delete&&h.push(s("delete",t.delete)),i.push(...p.pick??[]),r.push(...p.make??[]),u.push(...p.change??[]),h.push(...p.remove??[]);const v=[i,r,n.style??[],u,h].filter(e=>e.length>0),g=i.length+r.length+u.length+h.length===0,k=[{label:n.title,header:!0,onClick:()=>{}}];return g&&n.pinnedLine&&k.push({label:n.pinnedLine,header:!0,onClick:()=>{}}),v.forEach((e,t)=>{(t>0||g&&n.pinnedLine)&&k.push({label:"—",onClick:()=>{}}),k.push(...e)}),k}export{d as createNodeClipboard,v as nodeMenuItems};
1
+ import{DEFAULT_NODE_MENU_WORDS as e}from"../chunk-WK3AFPXY.js";export{canStep,fromItemSelector,parentStep,planDuplicate,planMove,planPaste,planRemove,planSetProps,planStep,toItemSelector,useNodeOps}from"../chunk-D2BCHTQR.js";import{TrashIcon as n,EyeIcon as t,EyeOffIcon as o,RenameIcon as p,PasteIcon as l,CopyIcon as c,MoveDownIcon as s,MoveUpIcon as a,DuplicateIcon as i,AddIcon as r,LayersIcon as u}from"../chunk-6EIVTKZZ.js";import"../chunk-KRXQM65X.js";import"../chunk-NKB3KVAB.js";import{jsx as h}from"react/jsx-runtime";function d(){let e=null;const n=/* @__PURE__ */new Set;return{get:()=>e,set(t){e=t;for(const e of[...n])e()},subscribe:e=>(n.add(e),()=>n.delete(e))}}var m={layer:{icon:/* @__PURE__ */h(u,{})},addInside:{icon:/* @__PURE__ */h(r,{})},duplicate:{icon:/* @__PURE__ */h(i,{})},moveUp:{icon:/* @__PURE__ */h(a,{})},moveDown:{icon:/* @__PURE__ */h(s,{})},copy:{icon:/* @__PURE__ */h(c,{}),shortcut:"⌘C"},cut:{icon:/* @__PURE__ */h(c,{}),shortcut:"⌘X"},paste:{icon:/* @__PURE__ */h(l,{}),shortcut:"⌘V"},rename:{icon:/* @__PURE__ */h(p,{})},hide:{icon:/* @__PURE__ */h(o,{})},show:{icon:/* @__PURE__ */h(t,{})},upLevel:{},delete:{icon:/* @__PURE__ */h(n,{}),danger:!0}};function v(n){const{actions:t,rows:o={},extras:p={},words:l=e}=n,c=e=>({...m[e],label:l[e],...o[e]}),s=(e,n,t)=>{const o=c(e),p={label:t??o.label??e,onClick:n};return o.icon&&(p.icon=o.icon),o.shortcut&&(p.shortcut=o.shortcut),o.danger&&(p.danger=!0),p},a=()=>{const e=c("layer"),t={label:e.label??l.layer,onClick:()=>{},children:n.layer.items};return e.icon&&(t.icon=e.icon),t},i=[],r=[],u=[],h=[],d=!!n.layer&&n.layer.items.length>1;if(d&&"pick"===(n.layer.group??"pick")&&i.push(a()),t.addInside&&r.push(s("addInside",t.addInside)),t.duplicate&&r.push(s("duplicate",t.duplicate)),t.moveUp&&r.push(s("moveUp",t.moveUp)),t.moveDown&&r.push(s("moveDown",t.moveDown)),t.copy&&r.push(s("copy",t.copy)),t.cut&&r.push(s("cut",t.cut)),t.paste){const e=c("paste").label??l.paste;r.push(s("paste",t.paste.run,l.pasteNamed({paste:e,label:t.paste.label})))}t.rename&&u.push(s("rename",t.rename)),t.hide&&u.push(t.hide.hidden?s("show",t.hide.run):s("hide",t.hide.run)),d&&"change"===n.layer.group&&u.push(a()),t.upLevel&&u.push(s("upLevel",t.upLevel)),t.delete&&h.push(s("delete",t.delete)),i.push(...p.pick??[]),r.push(...p.make??[]),u.push(...p.change??[]),h.push(...p.remove??[]);const v=[i,r,n.style??[],u,h].filter(e=>e.length>0),g=i.length+r.length+u.length+h.length===0,k=[{label:n.title,header:!0,onClick:()=>{}}];return g&&n.pinnedLine&&k.push({label:n.pinnedLine,header:!0,onClick:()=>{}}),v.forEach((e,t)=>{(t>0||g&&n.pinnedLine)&&k.push({label:"—",onClick:()=>{}}),k.push(...e)}),k}export{d as createNodeClipboard,v as nodeMenuItems};
@@ -1,6 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { W as WordFit } from '../wordFit-9B9nqyf3.js';
3
+ import { W as WordFit } from '../wordFit-D4S36PvB.js';
4
+ import { P as PanelWords } from '../words-CJjUKcV5.js';
4
5
  export { a as SETTINGS_BUCKETS, S as SettingsBucket } from '../buckets-CuLM47h9.js';
5
6
 
6
7
  /**
@@ -367,9 +368,10 @@ interface SettingsFieldProps {
367
368
  /** Set when the control is a single form element, so the label points at it. */
368
369
  htmlFor?: string;
369
370
  /**
370
- * A trailing control on the label row — a reset dot, a badge, or the SWITCH
371
- * of a boolean row (card 8801, Option A: a yes-or-no setting is one line, not
372
- * two, so the switch takes the spot a value like "34 px" normally sits in).
371
+ * A trailing control on the label row — `SettingsDefaultReset` (Default /
372
+ * Reset), or the SWITCH of a boolean row (card 8801, Option A: a yes-or-no
373
+ * setting is one line, not two, so the switch takes the spot a value like
374
+ * "34 px" normally sits in).
373
375
  */
374
376
  action?: ReactNode;
375
377
  /**
@@ -384,6 +386,41 @@ interface SettingsFieldProps {
384
386
  children?: ReactNode;
385
387
  className?: string;
386
388
  }
389
+ /**
390
+ * `SettingsDefaultReset` — the end of a label line that says whether the value
391
+ * follows its default (owner 2026-10-06: "Default", and a "Reset" button).
392
+ */
393
+ interface SettingsDefaultResetProps {
394
+ /** The field's visible label. The Reset button is named after it. */
395
+ label: string;
396
+ /** Is the value changed from its default? The host's to say. */
397
+ changed: boolean;
398
+ /** Clear the value back to its default. The part writes nothing itself. */
399
+ onReset: () => void;
400
+ /**
401
+ * What the default IS, in the merchant's words — "Site block gap — 24px".
402
+ * Shown as the tooltip: on Default as it is, on Reset as "Back to default: …".
403
+ * Read it live; a baked value goes stale the moment the default changes.
404
+ */
405
+ detail?: string;
406
+ /** Read-only (a sent campaign): Reset is drawn, and cannot be pressed. */
407
+ disabled?: boolean;
408
+ /**
409
+ * Rare: make "Default" a press of its own, for a row a merchant cannot step
410
+ * out of by hand. The website's type sizes are the case (GG 2026-10-06, Q3):
411
+ * their default can be a responsive `clamp()` no slider position stores, so
412
+ * one press keeps the section's own built-in size. `title` says what the
413
+ * press does; it is the tooltip and the accessible name, after "Default".
414
+ * Left out, Default presses nothing — the usual case.
415
+ */
416
+ defaultAction?: {
417
+ onPress: () => void;
418
+ title: string;
419
+ };
420
+ /** The host's words, from `useWords('panel', …)`. English when left out. */
421
+ words?: Partial<Pick<PanelWords, 'followsDefault' | 'reset' | 'resetName' | 'backToDefault'>>;
422
+ className?: string;
423
+ }
387
424
  /**
388
425
  * A switch has no text of its own, so without one of these it announces
389
426
  * "switch, on" and nothing else. The union makes an unnamed switch a COMPILE
@@ -901,6 +938,37 @@ declare function SettingsField({ label, labelId, icon, value, hint, changed, htm
901
938
  */
902
939
  declare function SettingsSwitch({ checked, onChange, disabled, id, ariaLabel, ariaLabelledBy, className, }: SettingsSwitchProps): react.JSX.Element;
903
940
 
941
+ /**
942
+ * The end of a setting's label line: **Default** while the value follows its
943
+ * default, a **Reset** button once it does not. ONE part, both editors.
944
+ *
945
+ * Owner, 2026-10-06. Before this the two editors said the same thing five ways:
946
+ * the website's "Inherits · Site" chip and "Overridden ↺" pill, its blue reset
947
+ * dot, and the email's "Default" mark, "Reset" link and "Reset to default"
948
+ * text. He picked the word **Default** for the resting state and the **Reset
949
+ * button** for the way back, in the same place on the row. It rides
950
+ * `SettingsField`'s `action` slot; `SettingsField` leaves its name alone (only a
951
+ * switch is named after the field).
952
+ *
953
+ * ONE ELEMENT, BOTH STATES. It is the same <button> whether it says Default or
954
+ * Reset, and that is for the keyboard: a press turns Reset into Default, and if
955
+ * Default were a <span> React would swap the node and focus would fall to the
956
+ * page, so the next Tab would start from the top. Resting, the button is
957
+ * `aria-disabled` and out of the tab order (`tabIndex={-1}`) — there is nothing
958
+ * to do there — but it keeps focus when a press has just put it there.
959
+ * `aria-disabled` and not `disabled`, because a `disabled` button drops focus
960
+ * too. `disabled` is kept for read-only, where a real disabled button is right.
961
+ *
962
+ * `defaultAction` is the one exception to "Default presses nothing": a row a
963
+ * merchant cannot leave by hand gets a press on the word itself, named after
964
+ * what it does (the website's type sizes, whose default can be a `clamp()`).
965
+ *
966
+ * CONTROLLED and hook-free like the rest of the panel: `changed` is the host's,
967
+ * and a press only asks the host to clear. The words are handed in (`words`),
968
+ * because a leaf never reads the strings provider.
969
+ */
970
+ declare function SettingsDefaultReset({ label, changed, onReset, detail, disabled, defaultAction, words, className, }: SettingsDefaultResetProps): react.JSX.Element;
971
+
904
972
  /**
905
973
  * A DRILL-IN ROW — a name, a one-line preview, and a chevron (card 8819).
906
974
  *
@@ -1085,4 +1153,4 @@ declare function SettingsCorners({ label, value, corners, onChange, id, classNam
1085
1153
  */
1086
1154
  declare function SettingsAction({ label, onPress, disabled, note, done, doneLabel, icon, id, className, }: SettingsActionProps): react.JSX.Element;
1087
1155
 
1088
- export { SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, type SettingsCornerPoint, SettingsCorners, type SettingsCornersProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsPlacement, type SettingsPlacementPoint, type SettingsPlacementProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
1156
+ export { SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, type SettingsCornerPoint, SettingsCorners, type SettingsCornersProps, SettingsDefaultReset, type SettingsDefaultResetProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsPlacement, type SettingsPlacementPoint, type SettingsPlacementProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
@@ -1,16 +1,18 @@
1
- export{SETTINGS_BUCKETS,SettingsField,SettingsGroup,SettingsSwitch,SettingsTabs}from"../chunk-67VZRQLG.js";import"../chunk-7A7O2JWQ.js";export{SettingsSegmented}from"../chunk-QB3BZIRA.js";import"../chunk-BZ5ASXOL.js";import{jsx as e,jsxs as a}from"react/jsx-runtime";function l({title:a,level:l=3,id:s,className:i}){/* @__PURE__ */
2
- return e(`h${l}`,{id:s,className:i?`es-group-head ${i}`:"es-group-head",children:a})}function s({title:a,level:l=4,id:s,className:i}){/* @__PURE__ */
3
- return e(`h${l}`,{id:s,className:i?`es-group-sub ${i}`:"es-group-sub",children:a})}function i({name:l,preview:s,offPage:i,status:n,onOpen:r,id:t,className:c}){const d=null!=s;/* @__PURE__ */
4
- return a("button",{type:"button",id:t,className:c?`es-drill ${c}`:"es-drill",onClick:r,children:[
5
- /* @__PURE__ */a("span",{className:"es-drill-text",children:[
6
- /* @__PURE__ */e("span",{className:"es-drill-name",children:l}),d?/* @__PURE__ */e("span",{className:i?"es-drill-preview is-offpage":"es-drill-preview",children:s}):null]}),n?/* @__PURE__ */e("span",{className:"es-drill-status",children:n}):null,
7
- /* @__PURE__ */e("span",{className:"es-drill-chev","aria-hidden":"true"})]})}function n({label:a,value:l,points:s,onChange:i,id:n,className:r}){/* @__PURE__ */
8
- return e("div",{id:n,className:"es-placement"+(r?` ${r}`:""),role:"radiogroup","aria-label":a,children:s.map(a=>{const s=l===a.value,n=!0===a.disabled;/* @__PURE__ */
9
- return e("button",{type:"button",role:"radio","aria-checked":s,"aria-label":a.label,"aria-disabled":n||void 0,disabled:n,title:a.title??a.label,className:`es-placement-cell${s?" is-active":""}${n?" is-off":""}`,onClick:()=>i(a.value),children:a.icon?/* @__PURE__ */e("span",{className:"es-placement-icon","aria-hidden":"true",children:a.icon}):/* @__PURE__ */e("span",{className:"es-placement-dot"})},a.value)})})}function r({label:l,value:s,corners:i,onChange:n,id:r,className:t}){/* @__PURE__ */
10
- return a("div",{id:r,className:"es-corners"+(t?` ${t}`:""),role:"group","aria-label":l,children:[
11
- /* @__PURE__ */e("span",{className:"es-corners-frame","aria-hidden":"true"}),i.map(a=>{const l=s.includes(a.value),i=!0===a.disabled;/* @__PURE__ */
12
- return e("button",{type:"button","aria-pressed":l,"aria-label":a.label,"aria-disabled":i||void 0,disabled:i,title:a.title??a.label,className:`es-corners-cell es-corners-${a.value}${l?" is-on":""}${i?" is-off":""}`,onClick:()=>n(l?s.filter(e=>e!==a.value):[...s,a.value]),children:/* @__PURE__ */e("span",{className:"es-corners-mark","aria-hidden":"true"})},a.value)})]})}function t({label:l,onPress:s,disabled:i,note:n,done:r,doneLabel:t,icon:c,id:d,className:o}){const u=void 0===t?l:t,m=r?u:l,p=null!=n,h=p&&d?`${d}-note`:void 0;/* @__PURE__ */
13
- return a("div",{className:o?`es-action-wrap ${o}`:"es-action-wrap",children:[
14
- /* @__PURE__ */a("button",{type:"button",id:d,className:r?"es-action is-done":"es-action",disabled:i,"aria-describedby":h,onClick:s,children:[c?/* @__PURE__ */e("span",{className:"es-action-icon","aria-hidden":"true",children:c}):null,
15
- /* @__PURE__ */e("span",{className:"es-action-label",children:m})]}),p?/* @__PURE__ */e("p",{id:h,className:"es-action-note",children:n}):null,
16
- /* @__PURE__ */e("span",{className:"es-action-live",role:"status","aria-live":"polite",children:r?u:null})]})}export{t as SettingsAction,r as SettingsCorners,i as SettingsDrill,l as SettingsGroupHead,s as SettingsGroupSub,n as SettingsPlacement};
1
+ export{SETTINGS_BUCKETS,SettingsField,SettingsGroup,SettingsTabs}from"../chunk-DICUMYMG.js";import{DEFAULT_PANEL_WORDS as e}from"../chunk-TRVZ7C23.js";export{SettingsSegmented,SettingsSwitch}from"../chunk-P6O363EE.js";import"../chunk-BZ5ASXOL.js";import{jsx as a,jsxs as l}from"react/jsx-runtime";function s({title:e,level:l=3,id:s,className:i}){/* @__PURE__ */
2
+ return a(`h${l}`,{id:s,className:i?`es-group-head ${i}`:"es-group-head",children:e})}function i({title:e,level:l=4,id:s,className:i}){/* @__PURE__ */
3
+ return a(`h${l}`,{id:s,className:i?`es-group-sub ${i}`:"es-group-sub",children:e})}function n({label:l,changed:s,onReset:i,detail:n,disabled:t,defaultAction:r,words:c,className:o}){const d={...e,...c},u=["es-default-reset"];if(s&&u.push("is-changed"),o&&u.push(o),!s&&r){u.push("is-pressable");const e=`${d.followsDefault}. ${r.title}`;/* @__PURE__ */
4
+ return a("button",{type:"button",className:u.join(" "),"aria-label":e,title:n?`${n}. ${r.title}`:r.title,disabled:t,onClick:r.onPress,children:d.followsDefault})}/* @__PURE__ */
5
+ return a("button",s?{type:"button",className:u.join(" "),"aria-label":d.resetName({label:l}),title:n?d.backToDefault({detail:n}):void 0,disabled:t,onClick:i,children:d.reset}:{type:"button",className:u.join(" "),"aria-disabled":"true",tabIndex:-1,title:n||void 0,children:d.followsDefault})}function t({name:e,preview:s,offPage:i,status:n,onOpen:t,id:r,className:c}){const o=null!=s;/* @__PURE__ */
6
+ return l("button",{type:"button",id:r,className:c?`es-drill ${c}`:"es-drill",onClick:t,children:[
7
+ /* @__PURE__ */l("span",{className:"es-drill-text",children:[
8
+ /* @__PURE__ */a("span",{className:"es-drill-name",children:e}),o?/* @__PURE__ */a("span",{className:i?"es-drill-preview is-offpage":"es-drill-preview",children:s}):null]}),n?/* @__PURE__ */a("span",{className:"es-drill-status",children:n}):null,
9
+ /* @__PURE__ */a("span",{className:"es-drill-chev","aria-hidden":"true"})]})}function r({label:e,value:l,points:s,onChange:i,id:n,className:t}){/* @__PURE__ */
10
+ return a("div",{id:n,className:"es-placement"+(t?` ${t}`:""),role:"radiogroup","aria-label":e,children:s.map(e=>{const s=l===e.value,n=!0===e.disabled;/* @__PURE__ */
11
+ return a("button",{type:"button",role:"radio","aria-checked":s,"aria-label":e.label,"aria-disabled":n||void 0,disabled:n,title:e.title??e.label,className:`es-placement-cell${s?" is-active":""}${n?" is-off":""}`,onClick:()=>i(e.value),children:e.icon?/* @__PURE__ */a("span",{className:"es-placement-icon","aria-hidden":"true",children:e.icon}):/* @__PURE__ */a("span",{className:"es-placement-dot"})},e.value)})})}function c({label:e,value:s,corners:i,onChange:n,id:t,className:r}){/* @__PURE__ */
12
+ return l("div",{id:t,className:"es-corners"+(r?` ${r}`:""),role:"group","aria-label":e,children:[
13
+ /* @__PURE__ */a("span",{className:"es-corners-frame","aria-hidden":"true"}),i.map(e=>{const l=s.includes(e.value),i=!0===e.disabled;/* @__PURE__ */
14
+ return a("button",{type:"button","aria-pressed":l,"aria-label":e.label,"aria-disabled":i||void 0,disabled:i,title:e.title??e.label,className:`es-corners-cell es-corners-${e.value}${l?" is-on":""}${i?" is-off":""}`,onClick:()=>n(l?s.filter(a=>a!==e.value):[...s,e.value]),children:/* @__PURE__ */a("span",{className:"es-corners-mark","aria-hidden":"true"})},e.value)})]})}function o({label:e,onPress:s,disabled:i,note:n,done:t,doneLabel:r,icon:c,id:o,className:d}){const u=void 0===r?e:r,p=t?u:e,m=null!=n,b=m&&o?`${o}-note`:void 0;/* @__PURE__ */
15
+ return l("div",{className:d?`es-action-wrap ${d}`:"es-action-wrap",children:[
16
+ /* @__PURE__ */l("button",{type:"button",id:o,className:t?"es-action is-done":"es-action",disabled:i,"aria-describedby":b,onClick:s,children:[c?/* @__PURE__ */a("span",{className:"es-action-icon","aria-hidden":"true",children:c}):null,
17
+ /* @__PURE__ */a("span",{className:"es-action-label",children:p})]}),m?/* @__PURE__ */a("p",{id:b,className:"es-action-note",children:n}):null,
18
+ /* @__PURE__ */a("span",{className:"es-action-live",role:"status","aria-live":"polite",children:t?u:null})]})}export{o as SettingsAction,c as SettingsCorners,n as SettingsDefaultReset,t as SettingsDrill,s as SettingsGroupHead,i as SettingsGroupSub,r as SettingsPlacement};
@@ -911,14 +911,69 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
911
911
  min-width: 0;
912
912
  }
913
913
 
914
+ /* The hint sits FLUSH LEFT, on the same edge as the row's icon and control —
915
+ not indented under the label's words (owner ruling, 2026-10-06; it was
916
+ 24px, glyph + gap, until then). `tests/panel-hint-flush-left` holds it. */
914
917
  .es-field-hint {
915
918
  margin: 8px 0 0;
916
- padding-left: 24px;
917
919
  font-size: 11px;
918
920
  line-height: 1.45;
919
921
  color: var(--es-muted);
920
922
  }
921
923
 
924
+ /* ── Default / Reset: the end of a label line ─────────────────────────────────
925
+ Owner 2026-10-06: a value that follows its default says "Default", and a
926
+ changed one offers a "Reset" button, in the same place. ONE element in both
927
+ states (see SettingsDefaultReset.tsx), so both states share the box: same
928
+ font, same padding, same height. Only the colour and the press change, and
929
+ the row does not jump when a press turns one into the other.
930
+
931
+ It sits at the END of the label line. With a value on the row, the value's
932
+ own `margin-left: auto` has already pushed both right, and the gap between
933
+ them is the head's own 8px; with no value, the part pushes itself. */
934
+ .es-default-reset {
935
+ appearance: none;
936
+ -webkit-appearance: none;
937
+ flex: none;
938
+ margin: 0 -4px 0 0;
939
+ padding: 2px 4px;
940
+ border: 0;
941
+ border-radius: 4px;
942
+ background: transparent;
943
+ font: inherit;
944
+ font-size: 11px;
945
+ font-weight: 500;
946
+ line-height: 12px;
947
+ color: var(--es-muted);
948
+ white-space: nowrap;
949
+ cursor: default;
950
+ }
951
+
952
+ .es-default-reset.is-changed {
953
+ color: var(--es-accent-text);
954
+ cursor: pointer;
955
+ }
956
+
957
+ .es-default-reset.is-changed:hover { background: var(--es-hover); }
958
+
959
+ .es-default-reset.is-changed:disabled {
960
+ color: var(--es-disabled);
961
+ background: transparent;
962
+ cursor: default;
963
+ }
964
+
965
+ .es-default-reset.is-pressable { cursor: pointer; }
966
+ .es-default-reset.is-pressable:hover { background: var(--es-hover); color: var(--es-text-secondary); }
967
+ .es-default-reset.is-pressable:disabled { color: var(--es-disabled); background: transparent; cursor: default; }
968
+
969
+ .es-default-reset:focus-visible {
970
+ outline: 2px solid var(--es-accent-ring);
971
+ outline-offset: 1px;
972
+ }
973
+
974
+ .es-field-head > .es-default-reset { margin-left: auto; }
975
+ .es-field-value + .es-default-reset { margin-left: 0; }
976
+
922
977
  /* A boolean row is ONE line (card 8801, Option A). The switch rides the label
923
978
  line in the `action` slot, so there is no control row under it — and with no
924
979
  control row, the label line's 7px bottom margin would be a gap to nothing.
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { P as PickWords } from '../words-B87GIFWH.js';
2
+ import { P as PickWords } from '../words-is_Vy6hn.js';
3
3
 
4
4
  /** One colour the site's theme names — offered first, by name. */
5
5
  type ThemeColour = {
@@ -29,7 +29,7 @@ type ColourPickerProps = {
29
29
  readOnly?: boolean;
30
30
  /**
31
31
  * The picker's own words in the host's language:
32
- * `useEditorStrings().pick.colour` (0.95.0). Left out, English. A prop, not
32
+ * `useEaselStrings().pick.colour` (0.95.0). Left out, English. A prop, not
33
33
  * the strings provider: the pickers import nothing outside their folder.
34
34
  */
35
35
  words?: PickWords['colour'];
@@ -54,6 +54,44 @@ type ColourPickerProps = {
54
54
  * to what is stored on Enter, Escape or leaving it.
55
55
  */
56
56
  declare function ColourPicker({ value, onChange, label, theme, caption, id: swatchId, readOnly, words, }: ColourPickerProps): react.JSX.Element;
57
+ type ColourWindowProps = {
58
+ /** The colour, `#rrggbb`, or `''` while there is none. */
59
+ value: string;
60
+ /** Live, as the colour moves: one `#rrggbb` from every way in. */
61
+ onChange: (next: string) => void;
62
+ /** The colour's name: the window is "<label> colour", its hex box "<label> hex code". */
63
+ label: string;
64
+ /** The theme's colours, offered first. */
65
+ theme?: readonly ThemeColour[];
66
+ /** Escape in the window. The opener closes it and takes the focus back. */
67
+ onClose?: () => void;
68
+ /** The window's DOM id, for the opener's `aria-controls`. */
69
+ id?: string;
70
+ readOnly?: boolean;
71
+ words?: PickWords['colour'];
72
+ /**
73
+ * Start the hex row with a swatch of the colour, as the text box's colour
74
+ * window draws it (E5b). Off by default: `ColourPicker`'s row already has its
75
+ * swatch beside the hex.
76
+ */
77
+ swatch?: boolean;
78
+ /**
79
+ * When a drag on the square or the hue bar writes. `'live'` (the default,
80
+ * as `ColourPicker` always has): every move. `'end'`: once, where the
81
+ * pointer comes up — one undo step for a host whose every write is one,
82
+ * like the text box (E5b). The thumbs, the hex and the swatch follow the
83
+ * drag live either way. An arrow key writes each press in both.
84
+ */
85
+ dragWrites?: 'live' | 'end';
86
+ };
87
+ /**
88
+ * THE COLOUR WINDOW on its own: the theme's colours, the saturation and
89
+ * brightness square, the hue strip and the hex. `ColourPicker` opens it under
90
+ * its swatch; a row that draws its own swatches (the text-styles list's Any
91
+ * color, plan step E6) opens the same one, so there is one window, not two.
92
+ * The opener owns when it is open and puts it in the panel's own flow.
93
+ */
94
+ declare function ColourWindow({ value, onChange, label, theme, onClose, id, readOnly, words, swatch, dragWrites, }: ColourWindowProps): react.JSX.Element;
57
95
 
58
96
  type ImageBoxProps = {
59
97
  /** The file's URL, or `''` while the setting has none. */
@@ -75,7 +113,7 @@ type ImageBoxProps = {
75
113
  readOnly?: boolean;
76
114
  /**
77
115
  * The box's own words in the host's language:
78
- * `useEditorStrings().pick.image` (0.95.0). Left out, English. A prop, not
116
+ * `useEaselStrings().pick.image` (0.95.0). Left out, English. A prop, not
79
117
  * the strings provider: the pickers import nothing outside their folder.
80
118
  */
81
119
  words?: PickWords['image'];
@@ -150,4 +188,4 @@ declare function hueFromPoint(x: number, box: {
150
188
  * name, so it reads as none rather than as its own bytes. */
151
189
  declare function mediaName(url: string): string;
152
190
 
153
- export { ColourPicker, type ColourPickerProps, HEX_MAX_LENGTH, type Hsv, ImageBox, type ImageBoxProps, PickWords, type ThemeColour, hexReadout, hexToHsv, hsvToHex, hueFromPoint, mediaName, svFromPoint, typedHex };
191
+ export { ColourPicker, type ColourPickerProps, ColourWindow, type ColourWindowProps, HEX_MAX_LENGTH, type Hsv, ImageBox, type ImageBoxProps, PickWords, type ThemeColour, hexReadout, hexToHsv, hsvToHex, hueFromPoint, mediaName, svFromPoint, typedHex };