efficient-easel 18.1.0 → 19.0.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 (112) hide show
  1. package/README.md +26 -1
  2. package/dist/{CanvasDragLayer-dZCPbQCP.d.ts → CanvasDragLayer-ARfaivCu.d.ts} +21 -3
  3. package/dist/{host-epHo7r8P.d.ts → ScreenDocumentContext-D55vIrG3.d.ts} +208 -208
  4. package/dist/autosave/index.d.ts +4 -4
  5. package/dist/{canvas-typing-BsNigaCa.d.ts → canvas-typing-Be4FO4q7.d.ts} +5 -5
  6. package/dist/chrome/chrome.css +2 -18
  7. package/dist/chrome/index.d.ts +5 -39
  8. package/dist/chrome/index.js +1 -1
  9. package/dist/{chunk-C2RHV42O.js → chunk-23DYNW6S.js} +1 -1
  10. package/dist/chunk-324PHMLC.js +8 -0
  11. package/dist/{chunk-HGV3FWOB.js → chunk-5DPIGWUL.js} +1 -1
  12. package/dist/{chunk-YLQYEDUQ.js → chunk-6DNDTEF6.js} +1 -1
  13. package/dist/chunk-AJSDWWJW.js +1 -0
  14. package/dist/chunk-C56MPJBH.js +1 -0
  15. package/dist/{chunk-N5AN2RF2.js → chunk-CM732ENS.js} +1 -1
  16. package/dist/{chunk-QEAUF632.js → chunk-CPFDJMFD.js} +12 -12
  17. package/dist/{chunk-S4AWJX4Q.js → chunk-EZG3TGQ7.js} +1 -1
  18. package/dist/chunk-EZKUTVQP.js +62 -0
  19. package/dist/{chunk-XHHVMAGM.js → chunk-FGYJ5DJY.js} +1 -1
  20. package/dist/chunk-J4MHNAED.js +5 -0
  21. package/dist/{chunk-VEZPDGDQ.js → chunk-JBOSEHIX.js} +1 -1
  22. package/dist/{chunk-RFE7ZG3Y.js → chunk-JC34NH4G.js} +1 -1
  23. package/dist/chunk-KRL3IUV3.js +1 -0
  24. package/dist/chunk-LNHT2KPX.js +2 -0
  25. package/dist/{chunk-SNB4LQMK.js → chunk-LUBJKUVV.js} +1 -1
  26. package/dist/chunk-M62P4IQK.js +2 -0
  27. package/dist/chunk-MS2U6MV5.js +1 -0
  28. package/dist/{chunk-7TOQCTHV.js → chunk-PY7OOQEZ.js} +3 -3
  29. package/dist/{chunk-KG7RJWOK.js → chunk-RYNBSKE7.js} +1 -1
  30. package/dist/{chunk-XDZHKYQX.js → chunk-SINAM2IT.js} +1 -1
  31. package/dist/{chunk-DXROGLB5.js → chunk-SP3TRDKA.js} +1 -1
  32. package/dist/{chunk-X52NFKUS.js → chunk-T5WDR5IE.js} +1 -1
  33. package/dist/{chunk-4QWTHRMD.js → chunk-WMQKV3DK.js} +4 -4
  34. package/dist/chunk-Z2V3TTAR.js +18 -0
  35. package/dist/chunk-ZG6PVMC3.js +19 -0
  36. package/dist/copy/index.d.ts +7 -7
  37. package/dist/copy/index.js +29 -29
  38. package/dist/drag/drag.css +23 -0
  39. package/dist/drag/index.d.ts +1 -1
  40. package/dist/drag/index.js +1 -1
  41. package/dist/easel/index.d.ts +2 -2
  42. package/dist/easel/index.js +1 -1
  43. package/dist/fields/fields.css +3 -3
  44. package/dist/fields/index.d.ts +1 -1
  45. package/dist/panel/index.d.ts +17 -84
  46. package/dist/panel/index.js +13 -19
  47. package/dist/panel/panel.css +41 -137
  48. package/dist/{panelWidths-D9hTLR25.d.ts → panelWidths-DgQXiPXU.d.ts} +34 -6
  49. package/dist/screen/components/AddBlockRow.js +1 -1
  50. package/dist/screen/components/AddSidePanel.js +1 -1
  51. package/dist/screen/components/MediaPopup.d.ts +21 -0
  52. package/dist/screen/components/MediaPopup.js +1 -0
  53. package/dist/screen/components/NodeContextMenu.d.ts +4 -4
  54. package/dist/screen/components/NodeContextMenu.js +1 -1
  55. package/dist/screen/components/PageEditGuardNotices.js +1 -1
  56. package/dist/screen/components/SaveControls.d.ts +6 -6
  57. package/dist/screen/components/SaveControls.js +1 -1
  58. package/dist/screen/components/ScreenChrome.js +1 -1
  59. package/dist/screen/components/ScreenDocumentContext.d.ts +1 -1
  60. package/dist/screen/components/ScreenDocumentContext.js +1 -1
  61. package/dist/screen/components/ScreenMount.d.ts +8 -8
  62. package/dist/screen/components/ScreenMount.js +1 -1
  63. package/dist/screen/components/ScreenStoreProvider.d.ts +7 -7
  64. package/dist/screen/components/ScreenStoreProvider.js +1 -1
  65. package/dist/screen/components/SectionScreenShell.js +17 -16
  66. package/dist/screen/components/SidePanelHead.js +1 -1
  67. package/dist/screen/components/panel-widths.d.ts +7 -6
  68. package/dist/screen/components/panel-widths.js +1 -1
  69. package/dist/screen/components/usePartBridge.d.ts +7 -7
  70. package/dist/screen/components/usePartBridge.js +1 -1
  71. package/dist/screen/index.d.ts +5 -5
  72. package/dist/screen/index.js +1 -1
  73. package/dist/screen/lib/document-addressing.d.ts +1 -1
  74. package/dist/screen/lib/host-inbound.js +1 -1
  75. package/dist/screen/lib/host.d.ts +5 -5
  76. package/dist/screen/lib/key-scope.js +1 -1
  77. package/dist/screen/lib/media-panel.d.ts +74 -37
  78. package/dist/screen/lib/media-panel.js +1 -1
  79. package/dist/screen/lib/media-picker.d.ts +5 -5
  80. package/dist/screen/lib/media-picker.js +1 -1
  81. package/dist/screen/lib/puck/device-fields.js +1 -1
  82. package/dist/screen/lib/puck/style-clipboard.d.ts +7 -7
  83. package/dist/screen/lib/puck/style-clipboard.js +1 -1
  84. package/dist/screen/lib/puck/use-section-ops.js +1 -1
  85. package/dist/screen/lib/save-status.d.ts +2 -2
  86. package/dist/screen/lib/screen-parts.d.ts +52 -11
  87. package/dist/screen/lib/screen-parts.js +1 -1
  88. package/dist/screen/screen.css +5 -4
  89. package/dist/screen/words.d.ts +3 -0
  90. package/dist/screen/words.js +1 -1
  91. package/dist/strings/index.d.ts +6 -6
  92. package/dist/strings/index.js +2 -2
  93. package/dist/{types-DT8ywrFQ.d.ts → types-B3iSHX-w.d.ts} +2 -22
  94. package/dist/{words-zH1H0WFw.d.ts → words-BeX5eGq7.d.ts} +2 -2
  95. package/dist/words-BpTjCfqO.d.ts +89 -0
  96. package/dist/{words-DkWcK1rh.d.ts → words-Cm8PMYn0.d.ts} +1 -1
  97. package/dist/{words-BpiVrsW5.d.ts → words-DYu4iBrR.d.ts} +1 -21
  98. package/package.json +9 -9
  99. package/dist/chunk-2ZWA6ARJ.js +0 -8
  100. package/dist/chunk-B4JN227A.js +0 -1
  101. package/dist/chunk-GWIVPLOX.js +0 -23
  102. package/dist/chunk-LLDHIBYG.js +0 -18
  103. package/dist/chunk-OBIL7BZO.js +0 -4
  104. package/dist/chunk-QNAPTJRM.js +0 -62
  105. package/dist/chunk-R6WTQWAC.js +0 -2
  106. package/dist/chunk-STWSCR25.js +0 -2
  107. package/dist/chunk-U277UX5I.js +0 -1
  108. package/dist/chunk-W6SDWCOL.js +0 -5
  109. package/dist/chunk-Z6PVRXVY.js +0 -1
  110. package/dist/screen/components/MediaSidePanel.d.ts +0 -49
  111. package/dist/screen/components/MediaSidePanel.js +0 -1
  112. package/dist/words-D-yhjdOg.d.ts +0 -183
@@ -1,9 +1,9 @@
1
1
  /*
2
2
  * efficient-easel/fields.css — the Inspector's own controls (blueprint 4.1).
3
3
  *
4
- * The rows, the tabs and the groups are `panel.css`'s (SettingsField,
5
- * SettingsTabs, SettingsGroup): the panel's rhythm is defined there ONCE
6
- * (`--es-row-gap`, `--es-rhythm-*`), so this file adds no spacing between rows
4
+ * The rows and the groups are `panel.css`'s (SettingsField, SettingsGroup),
5
+ * and the tabs are the shell's `Tabs`. The panel's rhythm is defined in
6
+ * `panel.css` ONCE (`--es-row-gap`, `--es-rhythm-*`), so this file adds no spacing between rows
7
7
  * and no spacing between groups. What it adds is what a row HOLDS where Puck
8
8
  * used to put the browser's own: a text box, a number box, a dropdown.
9
9
  *
@@ -233,7 +233,7 @@ interface InspectorProps {
233
233
  * with a glyph keeps its word visually hidden in its label (the shell's
234
234
  * documented icon-only pattern, `sr-only`), so the word is still its name,
235
235
  * and says the word in the tab's own Tooltip. A tab with no glyph shows its
236
- * word. Easel's `SettingsTabs` is no longer drawn here.
236
+ * word.
237
237
  * - A ROW ON ANOTHER TAB IS HIDDEN, NOT UNMOUNTED. The email designer's Logo
238
238
  * re-bakes its colours from a field on a tab you are not looking at; taking
239
239
  * the row out of the tree would stop that.
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { W as WordFit } from '../wordFit-7XpmhcaW.js';
4
- import { P as PanelWords } from '../words-zH1H0WFw.js';
4
+ import { P as PanelWords } from '../words-BeX5eGq7.js';
5
5
  export { BorderInput as SettingsBorder, BorderLineStyle as SettingsBorderLineStyle, BorderInputProps as SettingsBorderProps, BorderValue as SettingsBorderValue, CornerOption as SettingsCornerPoint, CornersInput as SettingsCorners, CornersInputProps as SettingsCornersProps, AlignmentGrid as SettingsPlacement, AlignmentPoint as SettingsPlacementPoint, AlignmentGridProps as SettingsPlacementProps, SidesInput as SettingsSides, SidesEachToggle as SettingsSidesEach, SidesInputProps as SettingsSidesProps, SidesValue as SettingsSidesValue, borderSides, borderStartEach, sidesStartEach } from 'efficient-shell/ui';
6
6
  export { a as DESIGN_GROUPS, D as DesignGroup, b as SETTINGS_BUCKETS, S as SettingsBucket } from '../buckets-Tj-nkBpg.js';
7
7
 
@@ -9,70 +9,9 @@ export { a as DESIGN_GROUPS, D as DesignGroup, b as SETTINGS_BUCKETS, S as Setti
9
9
  * The settings-panel primitives — the shared grammar BOTH editors' inspector
10
10
  * panels are built from (owner-approved design, 2026-08-20).
11
11
  *
12
- * Every one is CONTROLLED and hook-free: the host owns which tab is active and
13
- * which groups are open, exactly as the rail's host owns selection. That is what
12
+ * Every one is CONTROLLED and hook-free: the host owns which groups are open, exactly as the rail's host owns selection. That is what
14
13
  * keeps this a directive-free leaf a Next-16 server component can import.
15
14
  */
16
- /** One tab in the strip. `badge` shows a count (e.g. unsaved overrides). */
17
- interface SettingsTabItem {
18
- /** What this tab IS, as `activeId` and `onSelect` name it. Not a DOM id. */
19
- id: string;
20
- /**
21
- * What the tab is CALLED — and, since card 8810, its name in every tier.
22
- *
23
- * Required, and a plain string, on purpose. Give the tab an `icon` and a
24
- * narrow panel takes this word off the screen, but never out of the tab: it
25
- * stays in the markup (clipped, never `display: none`) as the tab's
26
- * accessible name at every width, and the shell's `Tooltip` says it on
27
- * hover. An icon-only control with no name is the exact defect card 8801
28
- * shipped and had to have caught in review, and a separate optional
29
- * `ariaLabel` prop is how that happens again. Here it cannot — the name is
30
- * the field the tab has always had, and it is not optional.
31
- */
32
- label: string;
33
- /**
34
- * An 18px glyph from `efficient-easel/icons`, so the tab can spend less width
35
- * than its word needs when the panel is dragged narrow (card 8810).
36
- *
37
- * Optional, and the strip is unchanged without it: a tab with no glyph has
38
- * nothing to fall back to, so it keeps its word at every width and the strip
39
- * wraps for it exactly as it did before. Pass icons for ALL the tabs in a
40
- * strip or none of them — a row that is half words and half glyphs below
41
- * 270px reads as a rendering fault, not as a decision.
42
- */
43
- icon?: ReactNode;
44
- badge?: number;
45
- /**
46
- * The DOM id of the element that shows this tab's settings — `aria-controls`.
47
- *
48
- * Without it a tab is announced as "tab, 1 of 4, selected" and there is no
49
- * panel it is bound to, which is what `role="tab"` promises there will be.
50
- * The shell cannot supply the id itself: only the host knows what element the
51
- * settings land in, and in Puck's flat sibling list that element may be one
52
- * the shell never rendered. Optional because a host that has no such element
53
- * to point at should render NO attribute rather than one pointing nowhere.
54
- */
55
- panelId?: string;
56
- /**
57
- * A DOM id for the tab BUTTON, so the panel above can name itself with
58
- * `aria-labelledby` and the pair is wired in both directions.
59
- */
60
- htmlId?: string;
61
- }
62
- interface SettingsTabsProps {
63
- items: SettingsTabItem[];
64
- /** The `id` of the tab currently shown. */
65
- activeId: string;
66
- onSelect: (id: string) => void;
67
- ariaLabel?: string;
68
- /**
69
- * Wires each tab to its panel, the shell's way: tab `tabButtonId(prefix, id)`,
70
- * `aria-controls` → `tabPanelId(prefix, id)` (both from efficient-shell/ui).
71
- * The host gives the panel it shows that id and names it with the tab's.
72
- */
73
- idPrefix?: string;
74
- className?: string;
75
- }
76
15
  /**
77
16
  * WHERE THIS STRIP'S WORDS STOP FITTING — the one thing the shell cannot know
78
17
  * about a segmented control, measured by the consumer that owns the words.
@@ -240,11 +179,12 @@ interface SettingsSegmentedOptionBase<V = string> {
240
179
  /** One option in a segmented control. */
241
180
  type SettingsSegmentedOption<V = string> = SettingsSegmentedOptionBase<V> & SettingsSegmentedLabel;
242
181
  /**
243
- * A SEGMENTED CONTROL — the tab strip's chip, with the other promise.
182
+ * A SEGMENTED CONTROL — the shell's chip, with the other promise.
244
183
  *
245
- * `SettingsTabs` is a TABLIST: its chips switch a panel, which is what
246
- * `role="tab"` promises. These chips switch nothing. They write a value and the
247
- * panel stays where it is, so they are pressed buttons in a group, and the
184
+ * A tab strip (the shell's `Tabs`) is a TABLIST: its chips switch a panel,
185
+ * which is what `role="tab"` promises. These chips switch nothing. They write
186
+ * a value and the panel stays where it is, so they are pressed buttons in a
187
+ * group, and the
248
188
  * marker is `aria-pressed` — the same one `efficient-storefront`'s own `Segmented`
249
189
  * has carried since the 2026-08-06 audit.
250
190
  *
@@ -593,14 +533,6 @@ type SettingsActionNote = {
593
533
  };
594
534
  type SettingsActionProps = SettingsActionBaseProps & SettingsActionNote;
595
535
 
596
- /**
597
- * @deprecated The selection panel draws the shell `Tabs` since 4.0.0. Still
598
- * used by WebsiteSettingsPanel (web-builder), HeaderSettingsPanel
599
- * (storefront-blocks) and the Site styles harness (storefront); a follow-up
600
- * card moves those and deletes this file.
601
- */
602
- declare function SettingsTabs({ items, activeId, onSelect, ariaLabel, idPrefix, className, }: SettingsTabsProps): react.JSX.Element;
603
-
604
536
  /**
605
537
  * A segmented control: one row of chips, one of them pressed.
606
538
  *
@@ -611,20 +543,20 @@ declare function SettingsTabs({ items, activeId, onSelect, ariaLabel, idPrefix,
611
543
  * stops fitting (below), and a name for a glyph — the shell's `Tooltip` around
612
544
  * it, never a `title`.
613
545
  *
614
- * THE SAME CHIP AS `SettingsTabs`, AND A DIFFERENT PROMISE. Everything a
615
- * merchant sees is shared — the grey track, the raised white chip for the one
616
- * he is on, the glyph drawn at every width, the word that drops where it stops
617
- * fitting. `panel.css` dresses both from one rule, so there is no second copy
618
- * of the chip grammar to drift.
546
+ * THE SAME CHIP AS A TAB STRIP, AND A DIFFERENT PROMISE. Everything a
547
+ * merchant sees is the shell's — the grey track, the chosen chip, the glyph
548
+ * drawn at every width — so there is no second copy of the chip grammar to
549
+ * drift.
619
550
  *
620
- * What differs is what the control SAYS it does. `SettingsTabs` is a tablist
551
+ * What differs is what the control SAYS it does. A tab strip (the shell's
552
+ * `Tabs`) is a tablist
621
553
  * and `role="tab"` promises that pressing a chip switches a panel. These chips
622
554
  * switch nothing: they write a value and the panel stays where it is. So this
623
555
  * is a group of pressed buttons, and its marker is `aria-pressed` — which is
624
556
  * also what the storefront's own `Segmented` has carried since the 2026-08-06
625
557
  * audit, so the two editors keep saying the same thing about the same control.
626
558
  *
627
- * That distinction is not decoration. Reach for `SettingsTabs` on a Weight row
559
+ * That distinction is not decoration. Reach for a tab strip on a Weight row
628
560
  * and a control that is correct today starts announcing "tab, 1 of 4, selected"
629
561
  * over a panel that is not there — three times in one part panel. It is the
630
562
  * same split the package already made between `SettingsGroup` and
@@ -675,7 +607,8 @@ declare function SettingsTabs({ items, activeId, onSelect, ariaLabel, idPrefix,
675
607
  declare function SettingsSegmented<V = string>({ options, value, onChange, ariaLabel, fit, disabled, className, }: SettingsSegmentedProps<V>): react.JSX.Element;
676
608
 
677
609
  /**
678
- * A group of settings under a soft pill row that folds with − / +.
610
+ * A group of settings under a grey band that folds with − / +: square, and
611
+ * edge to edge of the settings body (Lewis 2026-10-14, again 2026-10-23).
679
612
  *
680
613
  * The sign is drawn by `panel.css` from `aria-expanded`, so the open state has
681
614
  * exactly one home (the attribute assistive tech already reads) instead of a
@@ -940,4 +873,4 @@ declare function SettingsDrill({ name, preview, offPage, status, onOpen, id, cla
940
873
  */
941
874
  declare function SettingsAction({ label, onPress, disabled, note, done, doneLabel, icon, id, className, }: SettingsActionProps): react.JSX.Element;
942
875
 
943
- export { SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, SettingsDefaultReset, type SettingsDefaultResetProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps, type SettingsTabItem, SettingsTabs, type SettingsTabsProps };
876
+ export { SettingsAction, type SettingsActionBaseProps, type SettingsActionNote, type SettingsActionProps, SettingsDefaultReset, type SettingsDefaultResetProps, SettingsDrill, type SettingsDrillProps, SettingsField, type SettingsFieldProps, SettingsGroup, SettingsGroupHead, type SettingsGroupHeadProps, type SettingsGroupProps, SettingsGroupSub, type SettingsGroupSubProps, SettingsSegmented, type SettingsSegmentedFit, type SettingsSegmentedOption, type SettingsSegmentedProps, SettingsSwitch, type SettingsSwitchBaseProps, type SettingsSwitchLabel, type SettingsSwitchProps };
@@ -1,19 +1,13 @@
1
- export{DESIGN_GROUPS,SETTINGS_BUCKETS,SettingsGroup}from"../chunk-RC25EGHD.js";import{DEFAULT_PANEL_WORDS as e}from"../chunk-KFV7N2DJ.js";export{SettingsField,SettingsSegmented,SettingsSwitch}from"../chunk-36LVP5WD.js";import{withTip as t}from"../chunk-P2VEMZH5.js";import{Tabs as s,CountBadge as i,Tooltip as n}from"efficient-shell/ui";export{BorderInput as SettingsBorder,CornersInput as SettingsCorners,AlignmentGrid as SettingsPlacement,SidesInput as SettingsSides,SidesEachToggle as SettingsSidesEach,borderSides,borderStartEach,sidesStartEach}from"efficient-shell/ui";import{jsx as a,jsxs as l,Fragment as r}from"react/jsx-runtime";import{RevertIcon as d}from"efficient-shell/icons/efficient";function c(e){const t=e[0];if(!t?.htmlId||!t.htmlId.endsWith(`-tab-${t.id}`))return;const s=t.htmlId.slice(0,-`-tab-${t.id}`.length);return e.every(e=>e.htmlId===`${s}-tab-${e.id}`)?s:void 0}function o({items:t,activeId:n,onSelect:d,ariaLabel:o=e.settings,idPrefix:u,className:m}){
2
- /* @__PURE__ */
3
- return a("div",{className:"es-tabs-fit",children:/* @__PURE__ */a(s,{variant:"pill","aria-label":o,idPrefix:u??c(t),className:m?`es-tabs ${m}`:"es-tabs",value:n,onChange:d,items:t.map(e=>{const t="number"==typeof e.badge&&e.badge>0?/* @__PURE__ */a(i,{count:e.badge,tone:"accent"}):null;return e.icon?{id:e.id,tooltip:e.label,label:/* @__PURE__ */l(r,{children:[
4
- /* @__PURE__ */l("span",{className:"es-tab-tip",children:[
5
- /* @__PURE__ */a("span",{className:"es-tab-icon","aria-hidden":"true",children:e.icon}),
6
- /* @__PURE__ */a("span",{className:"es-tab-word has-icon",children:e.label})]}),t]})}:{id:e.id,label:/* @__PURE__ */l(r,{children:[
7
- /* @__PURE__ */a("span",{className:"es-tab-word",children:e.label}),t]})}})})})}function u({title:e,level:t=3,id:s,className:i}){/* @__PURE__ */
8
- return a(`h${t}`,{id:s,className:i?`es-group-head ${i}`:"es-group-head",children:e})}function m({title:e,level:t=4,id:s,className:i}){/* @__PURE__ */
9
- return a(`h${t}`,{id:s,className:i?`es-group-sub ${i}`:"es-group-sub",children:e})}function h({label:s,changed:i,onReset:n,detail:l,disabled:r,defaultAction:c,words:o,className:u}){const m={...e,...o},h=["es-default-reset"];if(i&&h.push("is-changed"),u&&h.push(u),!i&&c)return h.push("is-pressable"),t(c.title,/* @__PURE__ */a("button",{type:"button",className:h.join(" "),"aria-label":c.title,disabled:r,onClick:c.onPress,children:c.icon}));if(!i)/* @__PURE__ */
10
- return a("span",{className:h.join(" "),"aria-hidden":"true"});const N=m.resetName({label:s});return t(l?m.resetTitle({label:s,detail:l}):N,/* @__PURE__ */a("button",{type:"button",className:h.join(" "),"aria-label":N,"aria-description":l||void 0,disabled:r,onClick:e=>{n(),function(e){const t=e.currentTarget,s=t.closest(".es-field");let i=s?.querySelector(p(".es-field-ctl"))??s?.querySelector(p(".es-field-head"))??null,n=s?null:t.parentElement;for(let e=0;!i&&n&&e<f;e+=1,n=n.parentElement)i=[...n.querySelectorAll(b)].find(e=>e!==t)??null;i?.focus()}(e)},children:/* @__PURE__ */a(d,{size:15})}))}var b=['input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])","button:not([disabled]):not(.es-default-reset)",'[tabindex]:not([tabindex="-1"])'].join(", "),p=e=>b.split(", ").map(t=>`${e} ${t}`).join(", "),f=4;function N({name:e,preview:t,offPage:s,status:i,onOpen:d,id:c,className:o}){const u=null!=t;/* @__PURE__ */
11
- return l("button",{type:"button",id:c,className:o?`es-drill ${o}`:"es-drill",onClick:d,children:[
12
- /* @__PURE__ */l(n,{content:u?/* @__PURE__ */l(r,{children:[e,
13
- /* @__PURE__ */a("br",{}),t]}):e,describe:!1,className:"es-drill-text",children:[
14
- /* @__PURE__ */a("span",{className:"es-drill-name",children:e}),u?/* @__PURE__ */a("span",{className:s?"es-drill-preview is-offpage":"es-drill-preview",children:t}):null]}),i?/* @__PURE__ */a("span",{className:"es-drill-status",children:i}):null,
15
- /* @__PURE__ */a("span",{className:"es-drill-chev","aria-hidden":"true"})]})}function g({label:e,onPress:t,disabled:s,note:i,done:n,doneLabel:r,icon:d,id:c,className:o}){const u=void 0===r?e:r,m=n?u:e,h=null!=i,b=h&&c?`${c}-note`:void 0;/* @__PURE__ */
16
- return l("div",{className:o?`es-action-wrap ${o}`:"es-action-wrap",children:[
17
- /* @__PURE__ */l("button",{type:"button",id:c,className:n?"es-action is-done":"es-action",disabled:s,"aria-describedby":b,onClick:t,children:[d?/* @__PURE__ */a("span",{className:"es-action-icon","aria-hidden":"true",children:d}):null,
18
- /* @__PURE__ */a("span",{className:"es-action-label",children:m})]}),h?/* @__PURE__ */a("p",{id:b,className:"es-action-note",children:i}):null,
19
- /* @__PURE__ */a("span",{className:"es-action-live",role:"status","aria-live":"polite",children:n?u:null})]})}export{g as SettingsAction,h as SettingsDefaultReset,N as SettingsDrill,u as SettingsGroupHead,m as SettingsGroupSub,o as SettingsTabs};
1
+ export{DESIGN_GROUPS,SETTINGS_BUCKETS,SettingsGroup}from"../chunk-RC25EGHD.js";import{DEFAULT_PANEL_WORDS as e}from"../chunk-KFV7N2DJ.js";export{SettingsField,SettingsSegmented,SettingsSwitch}from"../chunk-36LVP5WD.js";import{withTip as s}from"../chunk-P2VEMZH5.js";import{jsx as t,jsxs as n,Fragment as i}from"react/jsx-runtime";import{RevertIcon as l}from"efficient-shell/icons/efficient";import{Tooltip as a}from"efficient-shell/ui";export{BorderInput as SettingsBorder,CornersInput as SettingsCorners,AlignmentGrid as SettingsPlacement,SidesInput as SettingsSides,SidesEachToggle as SettingsSidesEach,borderSides,borderStartEach,sidesStartEach}from"efficient-shell/ui";function r({title:e,level:s=3,id:n,className:i}){/* @__PURE__ */
2
+ return t(`h${s}`,{id:n,className:i?`es-group-head ${i}`:"es-group-head",children:e})}function o({title:e,level:s=4,id:n,className:i}){/* @__PURE__ */
3
+ return t(`h${s}`,{id:n,className:i?`es-group-sub ${i}`:"es-group-sub",children:e})}function d({label:n,changed:i,onReset:a,detail:r,disabled:o,defaultAction:d,words:m,className:h}){const b={...e,...m},f=["es-default-reset"];if(i&&f.push("is-changed"),h&&f.push(h),!i&&d)return f.push("is-pressable"),s(d.title,/* @__PURE__ */t("button",{type:"button",className:f.join(" "),"aria-label":d.title,disabled:o,onClick:d.onPress,children:d.icon}));if(!i)/* @__PURE__ */
4
+ return t("span",{className:f.join(" "),"aria-hidden":"true"});const N=b.resetName({label:n});return s(r?b.resetTitle({label:n,detail:r}):N,/* @__PURE__ */t("button",{type:"button",className:f.join(" "),"aria-label":N,"aria-description":r||void 0,disabled:o,onClick:e=>{a(),function(e){const s=e.currentTarget,t=s.closest(".es-field");let n=t?.querySelector(u(".es-field-ctl"))??t?.querySelector(u(".es-field-head"))??null,i=t?null:s.parentElement;for(let e=0;!n&&i&&e<p;e+=1,i=i.parentElement)n=[...i.querySelectorAll(c)].find(e=>e!==s)??null;n?.focus()}(e)},children:/* @__PURE__ */t(l,{size:15})}))}var c=['input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])","button:not([disabled]):not(.es-default-reset)",'[tabindex]:not([tabindex="-1"])'].join(", "),u=e=>c.split(", ").map(s=>`${e} ${s}`).join(", "),p=4;function m({name:e,preview:s,offPage:l,status:r,onOpen:o,id:d,className:c}){const u=null!=s;/* @__PURE__ */
5
+ return n("button",{type:"button",id:d,className:c?`es-drill ${c}`:"es-drill",onClick:o,children:[
6
+ /* @__PURE__ */n(a,{content:u?/* @__PURE__ */n(i,{children:[e,
7
+ /* @__PURE__ */t("br",{}),s]}):e,describe:!1,className:"es-drill-text",children:[
8
+ /* @__PURE__ */t("span",{className:"es-drill-name",children:e}),u?/* @__PURE__ */t("span",{className:l?"es-drill-preview is-offpage":"es-drill-preview",children:s}):null]}),r?/* @__PURE__ */t("span",{className:"es-drill-status",children:r}):null,
9
+ /* @__PURE__ */t("span",{className:"es-drill-chev","aria-hidden":"true"})]})}function h({label:e,onPress:s,disabled:i,note:l,done:a,doneLabel:r,icon:o,id:d,className:c}){const u=void 0===r?e:r,p=a?u:e,m=null!=l,h=m&&d?`${d}-note`:void 0;/* @__PURE__ */
10
+ return n("div",{className:c?`es-action-wrap ${c}`:"es-action-wrap",children:[
11
+ /* @__PURE__ */n("button",{type:"button",id:d,className:a?"es-action is-done":"es-action",disabled:i,"aria-describedby":h,onClick:s,children:[o?/* @__PURE__ */t("span",{className:"es-action-icon","aria-hidden":"true",children:o}):null,
12
+ /* @__PURE__ */t("span",{className:"es-action-label",children:p})]}),m?/* @__PURE__ */t("p",{id:h,className:"es-action-note",children:l}):null,
13
+ /* @__PURE__ */t("span",{className:"es-action-live",role:"status","aria-live":"polite",children:a?u:null})]})}export{h as SettingsAction,d as SettingsDefaultReset,m as SettingsDrill,r as SettingsGroupHead,o as SettingsGroupSub};
@@ -175,23 +175,13 @@
175
175
  --es-track-gap: 2px;
176
176
  }
177
177
 
178
- /* ── the tab strip: a segmented control ────────────────────────────────────── */
179
-
180
- /* The strip's own container context (card 8810), and the reason `SettingsTabs`
181
- renders a wrapper at all.
182
-
183
- The drop rules further down turn on the width of the PANEL, and `@container`
184
- is the only thing that can ask for it. A media query asks the WINDOW, and the
185
- window is the wrong box: the inspector is a column the merchant drags, so a
186
- 220px panel on a 2560px screen is the ordinary case, not an edge one — a
187
- media query would answer for that panel with the width of the screen behind
188
- it.
189
-
190
- The context lives HERE rather than in a host's own panel rule so the shell is
191
- self-contained: a host that never declares `container-type` would silently
192
- pin the strip to one state with nothing to see and nothing to fail. (The
193
- top-bar rail's words are measured by the shell's IconRail since 4.165.0.) */
194
- .es-tabs-fit,
178
+ /* ── the segmented control's wrapper ──────────────────────────────────────── */
179
+
180
+ /* The strip's own container context. Its words drop on the width of the
181
+ PANEL, and `@container`-sized measurements (`100cqi`) are the only thing that
182
+ can ask for it: the inspector is a column the merchant drags, so a window
183
+ media query would answer with the width of the wrong box. The context lives
184
+ HERE so a host that never declares `container-type` cannot pin the strip. */
195
185
  .es-segs-fit {
196
186
  container-type: inline-size;
197
187
  flex: none;
@@ -235,21 +225,15 @@
235
225
  makes that meaningless here — without it the strip cannot shrink below the
236
226
  basis the parent gives it.
237
227
 
238
- BOTH `-fit` WRAPPERS, though only the segmented one has a live consumer
239
- today. Measured in the shop at the time of writing: 1845 custom fields
240
- rendered, 68 draw a tab strip, 0 of them inside `.es-field-ctl`. But
241
- `PanelTabs` already draws a strip AMONG the fields and is one refactor from
242
- the slot, and the two wrappers share the declaration that causes this — a
243
- rule that fixed one and left the other would be a trap wearing a fix. */
244
- .es-field-ctl > .es-tabs-fit,
228
+ (The tab strip's `.es-tabs-fit` carried the same exception until easel's
229
+ `SettingsTabs` was deleted; a tab strip is the shell's `Tabs` now.) */
245
230
  .es-field-ctl > .es-segs-fit {
246
231
  flex: 1 1 auto;
247
232
  min-width: 0;
248
233
  }
249
234
 
250
235
  /* THE TRACK AND ITS CHIPS ARE THE SHELL'S (efficient-easel issue 199 step 4, harness
251
- UI-24). `SettingsTabs` is the shell's `Tabs` (`pill`), `SettingsSegmented`
252
- its `Segmented`: the grey track, the chosen chip filled with the accent (card
236
+ UI-24). `SettingsSegmented` is the shell's `Segmented`: the grey track, the chosen chip filled with the accent (card
253
237
  66138's segmented look, Lewis 2026-10-06's blue), the quiet chips' ink, the
254
238
  focus ring, a count (`CountBadge`) and a chip out of force are the kit's.
255
239
  What stays here is LAYOUT the shell has no prop for:
@@ -259,31 +243,15 @@
259
243
  one row by definition, so it never wraps. A SEGMENT TAKES ITS OWN WIDTH, THEN
260
244
  SHARES WHAT IS LEFT (`1 1 auto`, not equal segments: three equal segments
261
245
  need a 267px track against the 281px the shop's default panel gives). */
262
- .es-tabs,
263
246
  .es-segs {
264
247
  display: flex;
265
248
  flex-wrap: nowrap;
266
249
  width: 100%;
267
250
  }
268
- .es-tabs > [role='tab'],
269
251
  .es-segs > button {
270
252
  flex: 1 1 auto;
271
253
  white-space: nowrap;
272
254
  }
273
- /* A glyph tab stands in the shell Tooltip's wrapper (`TabItem.tooltip`,
274
- efficient-shell 4.166.0): the wrapper is the strip's item in the tab's
275
- place, and the tab fills it. The wrapper adds no width of its own, so the
276
- thresholds below are the tab's, as measured. */
277
- .es-tabs > :not([role='tab']) {
278
- display: flex;
279
- flex: 1 1 auto;
280
- min-width: 0;
281
- }
282
- .es-tabs > :not([role='tab']) > [role='tab'] {
283
- flex: 1 1 auto;
284
- white-space: nowrap;
285
- }
286
-
287
255
  /* The glyph in the chip: 16px of drawing, the same size a field's icon takes,
288
256
  standing in an 18px box.
289
257
 
@@ -301,7 +269,6 @@
301
269
 
302
270
  `flex: none` because a glyph that shrinks is a squeezed segment in a smaller
303
271
  box. */
304
- .es-tab-icon,
305
272
  .es-seg-icon {
306
273
  display: inline-flex;
307
274
  width: var(--es-chip-icon);
@@ -311,106 +278,24 @@
311
278
  justify-content: center;
312
279
  }
313
280
 
314
- /* A glyph tab's glyph and word, side by side in `.es-tab-tip` (a hook since
315
- 1.0.0; the word's Tooltip is the tab's own since efficient-shell 4.166.0):
316
- the same `gap-1.5` the shell's tab puts between its children, so the
317
- measured thresholds below still hold. A clipped word is out of the flow,
318
- so a glyph-only tab keeps no gap. */
319
- .es-tab-tip {
320
- display: inline-flex;
321
- align-items: center;
322
- gap: calc(1.5 * var(--spacing));
323
- }
324
-
325
- .es-tab-icon svg,
326
281
  .es-seg-icon svg { width: var(--es-chip-icon); height: var(--es-chip-icon); display: block; }
327
282
 
328
- /* ── where the word stops fitting ──────────────────────────────────────────── */
329
-
330
- /* RE-MEASURED ON THE SHELL'S PILL (efficient-easel issue 199, part C, 2026-10-10).
331
- * The chip is efficient-shell's `Tabs` pill since step 4, wider than the 0.106
332
- * chip the old numbers were taken on, so the thresholds were measured again: in
333
- * headless Chromium, against the COMPILED shell CSS and these sheets with their
334
- * @container tiers taken out, the shop's four buckets Content / Media / Style /
335
- * Layout with 16px glyphs, by EDGE GEOMETRY — the right edge of the last tab
336
- * against the right content edge of the track (not `scrollWidth`, which says
337
- * "fits" about a flex button drawing past itself).
338
- *
339
- * Smallest container width with nothing drawn past the track edge:
340
- *
341
- * tabs words glyph + active word glyphs only
342
- * 2 197px 151px 92px
343
- * 3 280px 195px 136px
344
- * 4 375px 239px 180px
345
- *
346
- * The glyphs-only column is arithmetic (`n` chips of a 16px glyph in `px-3`,
347
- * `gap-1` apart in a `p-1` track) and the measurement agrees with it to the
348
- * pixel, which is what says the method is sound; `tests/panel-layout.test.ts`
349
- * (d) pins the arithmetic.
350
- *
351
- * The drag band gives containers from 181px (a 220px panel, the minimum) to
352
- * 281px (a 320px panel, the default). Read against it:
353
- *
354
- * · TWO tabs now need 197px for both words, so at the narrow end of the band
355
- * the tab not being read loses its word (it never did on the old chip).
356
- * · THREE tabs carry their words down to 280px — the default panel, just —
357
- * then one word down to 195px, then glyphs.
358
- * · FOUR tabs need 375px, which the band never reaches: glyph-plus-one-word
359
- * down to 239px, then glyphs, which fit the 181px floor.
360
- *
361
- * WHY THE RULES ARE KEYED ON THE TAB COUNT. With natural widths, whether the
362
- * words fit is a question about the SUM of the labels, so the answer moves with
363
- * how many buckets a section has. A width-only rule would have to answer for
364
- * the worst case — four tabs — and would then strip the word off a TWO-tab
365
- * strip at the default panel width.
366
- *
367
- * Only `.has-icon` is ever touched. A tab with no glyph has nothing to fall
368
- * back to, so it keeps its word at every width — a button holding neither word
369
- * nor glyph is not a narrower segment, it is an empty one. A word is clipped,
370
- * never `display: none`: the shell's tab takes its name from it. */
371
-
372
- /* TWO OR MORE, under 197px: the word on the tab he is not reading goes. */
373
- @container (width < 197px) {
374
- .es-tabs:has(> :nth-child(2)) [role='tab']:not([aria-selected='true']) .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
375
- }
376
-
377
- /* THREE OR MORE, under 280px. */
378
- @container (width < 280px) {
379
- .es-tabs:has(> :nth-child(3)) [role='tab']:not([aria-selected='true']) .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
380
- }
381
-
382
- /* FOUR OR MORE, under 375px: a fourth label is another ~95px of track. */
383
- @container (width < 375px) {
384
- .es-tabs:has(> :nth-child(4)) [role='tab']:not([aria-selected='true']) .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
385
- }
386
-
387
- /* THREE OR MORE at the narrow end, under 195px: the last word goes as well. */
388
- @container (width < 195px) {
389
- .es-tabs:has(> :nth-child(3)) [role='tab'][aria-selected='true'] .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
390
- }
391
-
392
- /* FOUR OR MORE, under 239px: the strip is four glyphs on a track. It reads —
393
- the track and the chips are what say "these are buttons". */
394
- @container (width < 239px) {
395
- .es-tabs:has(> :nth-child(4)) [role='tab'][aria-selected='true'] .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
396
- }
397
-
398
283
  /* ── the segmented control: the same chip, a different promise ─────────────── */
399
284
 
400
- /* WHY A SECOND COMPONENT AND NOT A SECOND CALLER OF `SettingsTabs` (card 66248).
401
- `SettingsTabs` is a TABLIST, and `role="tab"` is the promise that pressing a
285
+ /* WHY NOT A TAB STRIP (card 66248). A tab strip (the shell's `Tabs`) is a
286
+ TABLIST, and `role="tab"` is the promise that pressing a
402
287
  chip switches a panel. The rows this control is for switch nothing: Weight,
403
288
  Case and Alignment WRITE A VALUE and stay where they are. Pointing the tab
404
289
  strip at them would make three controls that are correct today start
405
290
  announcing "tab, 1 of 4, selected" over nothing. Everything a merchant SEES
406
- is shared above — one chip, one track, one raised-chip mark — and only the
407
- promise differs. It is the split this sheet already made between
291
+ is the shell's chip and track, and only the promise differs. It is the split this sheet already made between
408
292
  `.es-group-head` and `button.es-group-head`.
409
293
 
410
- ── WHY THE @container TIERS ABOVE ARE NOT INHERITED ────────────────────────
294
+ ── WHY NOT FIXED @container TIERS ──────────────────────────────────────────
411
295
 
412
- Those three tiers are keyed on the tab COUNT, and that works for the strip
413
- they were measured for because the shell KNOWS its four words: Content /
296
+ Easel's old tab strip (`SettingsTabs`, deleted) dropped its words at fixed
297
+ tiers keyed on the tab COUNT. That worked for the strip they were measured
298
+ for because it KNEW its four words: Content /
414
299
  Media / Style / Layout. It cannot work here, and the reason is arithmetic,
415
300
  not taste. MEASURED in Chromium, `tests/fixtures/segmented-fit.html`, the
416
301
  width at which each row's words stop fitting:
@@ -424,8 +309,8 @@
424
309
  181px and 281px, so the wrong one is wrong ON SCREEN: at the default 281px
425
310
  panel Alignment's words fit and Case's do not.
426
311
 
427
- Whether the words fit is a question about the SUM OF THE LABELS, which is
428
- what this sheet already says out loud above, and a count is only ever a
312
+ Whether the words fit is a question about the SUM OF THE LABELS, and a
313
+ count is only ever a
429
314
  stand-in for it. The stand-in holds while one strip's words are known and
430
315
  breaks the moment a second strip brings its own.
431
316
 
@@ -447,8 +332,8 @@
447
332
  property instead of in the query — subtract the threshold from the width and
448
333
  scale hard. Above the threshold that is a large positive length and the word
449
334
  is unlimited; below it the result is negative, and `max-width` clamps a
450
- negative to zero, which is the word gone. Same two tiers as the strip above,
451
- measured continuously instead of at three fixed steps.
335
+ negative to zero, which is the word gone. The same two steps the old tab
336
+ strip had, measured continuously instead of at fixed widths.
452
337
 
453
338
  WHY NOT ESTIMATE THE WORDS FROM THEIR LETTERS, and skip the measuring. The
454
339
  chip's font is `--es-font`, which resolves to SF on a Mac, Segoe UI on
@@ -620,7 +505,9 @@
620
505
  font-weight: 600;
621
506
  color: var(--es-text);
622
507
  background: var(--es-hover);
623
- border-radius: var(--es-row-radius);
508
+ /* SQUARE (Lewis 2026-10-14, again 2026-10-23): a band, not a pill.
509
+ `tests/panel-group-band-full-bleed.test.ts` fails on any radius here. */
510
+ border-radius: 0;
624
511
  /* A menu row tall (Lewis's row heights, 2026-10-12): 36, whatever the
625
512
  words' line height. */
626
513
  min-height: var(--row-height-menu, 36px);
@@ -634,6 +521,23 @@
634
521
  margin: max(var(--es-row-gap), var(--es-rhythm-group)) 0 0;
635
522
  }
636
523
 
524
+ /* FULL BLEED IN THE SETTINGS BODY (Lewis 2026-10-14, again 2026-10-23): the
525
+ band's ground reaches both edges of the panel, with no inset round it. It
526
+ cancels the body's side inset and pads the same amount back inside, so its
527
+ words start on the rows' left edge and its − / + ends on their right edge.
528
+ The start side is the panel's one inset (`--es-insp-pad-x`, chrome.css). The
529
+ end side is `--es-insp-pad-end` where a host keeps a scrollbar lane out of
530
+ the inset (the website editor's body pads its end by the inset less the
531
+ lane), else the same inset: a band that bled the full inset there would run
532
+ the lane's width past the body and give it a sideways scroll.
533
+ ONLY IN `.es-insp-body`: that is the one place the inset is known. A host
534
+ that draws a band anywhere else gets the square band in its own box. */
535
+ .es-insp-body .es-group-head {
536
+ width: calc(100% + var(--es-insp-pad-x) + var(--es-insp-pad-end, var(--es-insp-pad-x)));
537
+ margin-inline: calc(-1 * var(--es-insp-pad-x)) calc(-1 * var(--es-insp-pad-end, var(--es-insp-pad-x)));
538
+ padding-inline: var(--es-insp-pad-x) var(--es-insp-pad-end, var(--es-insp-pad-x));
539
+ }
540
+
637
541
  /* THE QUIET TIER (card 66300) — a heading one rung under the pill.
638
542
  NO GROUND AND NO RULE, which is the whole point of it. The pill separates
639
543
  itself from the settings around it with a fill; this separates itself with
@@ -11,8 +11,18 @@ import { CSSProperties } from 'react';
11
11
  * one pixel under the 270 the strip's widest tier needs, so the tab words
12
12
  * turned into glyphs at the width every merchant opens on.
13
13
  *
14
- * MIN_PANEL_W is load-bearing on the top bar: six 34px rail buttons are 204px,
15
- * and at 220 the rail column spreads them with nothing to spare.
14
+ * THE FLOOR IS PER SIDE (owner ruling A4g, 2026-10-13): LEFT 240, RIGHT 220.
15
+ * At 220 the Layers head's three buttons ("+", fold all, hide panel) did not
16
+ * fit beside its title: they cut it to "La…" then, and since the head's
17
+ * "title wins" rule (2026-10-14) they drop to a second row instead (the head
18
+ * is 100px tall, not 60). Title and buttons share one row from 231 (measured
19
+ * in Chrome, 2026-10-22). The ruling raised the left floor only, so the
20
+ * right panel still drags down to 220.
21
+ *
22
+ * The left floor is load-bearing on the top bar: the rail column is the left
23
+ * panel's width (chrome.css `.es-cluster--rail`), and six 34px rail buttons
24
+ * are 204px. At 220 they filled the column with nothing to spare; at 240 a
25
+ * seventh (238px) just fits.
16
26
  *
17
27
  * The campaign designer opened at 288 / 288 with a 520 ceiling until the
18
28
  * 2026-09-17 redesign. One set of numbers now, here.
@@ -21,7 +31,19 @@ declare const DEFAULT_PANEL_W: {
21
31
  readonly left: 288;
22
32
  readonly right: 320;
23
33
  };
24
- declare const MIN_PANEL_W = 220;
34
+ /** The narrowest each side panel drags to (px): left 240, right 220 (A4g). */
35
+ declare const MIN_PANEL_W_BY_SIDE: {
36
+ readonly left: 240;
37
+ readonly right: 220;
38
+ };
39
+ /**
40
+ * The lower of the two floors (220), the one both panels shared before A4g.
41
+ *
42
+ * @deprecated A floor is per side now: read `MIN_PANEL_W_BY_SIDE.left` or
43
+ * `.right`. Kept, at its old number, for hosts that import it; it is the
44
+ * right panel's floor, and no longer the left one's.
45
+ */
46
+ declare const MIN_PANEL_W: 220;
25
47
  declare const MAX_PANEL_W = 560;
26
48
  /** Which side panel a width belongs to. */
27
49
  type PanelSide = 'left' | 'right';
@@ -29,8 +51,14 @@ type PanelWidths = {
29
51
  left: number;
30
52
  right: number;
31
53
  };
32
- /** Clamp a width into the panel band and round to a whole pixel. */
33
- declare function clampPanelWidth(width: number): number;
54
+ /**
55
+ * Clamp a width into a side panel's band and round to a whole pixel: from that
56
+ * side's floor (`MIN_PANEL_W_BY_SIDE`) to `MAX_PANEL_W`.
57
+ *
58
+ * Without a side it clamps from the lower floor, 220, as before A4g. Pass
59
+ * the side: a left width clamped without it can stay under 240.
60
+ */
61
+ declare function clampPanelWidth(width: number, side?: PanelSide): number;
34
62
  /**
35
63
  * The inline style that carries the widths, as `--es-left-w` / `--es-right-w`.
36
64
  *
@@ -63,4 +91,4 @@ declare function panelWidthVars(widths: PanelWidths): CSSProperties;
63
91
  */
64
92
  declare function setPanelWidthVars(el: HTMLElement, widths: PanelWidths): void;
65
93
 
66
- export { DEFAULT_PANEL_W as D, MAX_PANEL_W as M, type PanelSide as P, MIN_PANEL_W as a, type PanelWidths as b, clampPanelWidth as c, panelWidthVars as p, setPanelWidthVars as s };
94
+ export { DEFAULT_PANEL_W as D, MAX_PANEL_W as M, type PanelSide as P, MIN_PANEL_W as a, MIN_PANEL_W_BY_SIDE as b, type PanelWidths as c, clampPanelWidth as d, panelWidthVars as p, setPanelWidthVars as s };
@@ -1 +1 @@
1
- export{AddBlockRow as default}from"../../chunk-N5AN2RF2.js";import"../../chunk-M2Z775ZC.js";import"../../chunk-W6SDWCOL.js";import"../../chunk-R6WTQWAC.js";import"../../chunk-U3JP4VGF.js";import"../../chunk-C2RHV42O.js";import"../../chunk-PLVEGYK6.js";import"../../chunk-QEAUF632.js";import"../../chunk-TBY3DFKS.js";import"../../chunk-5ZBV5RA6.js";import"../../chunk-BPXEFVSQ.js";import"../../chunk-A6Z4SD3I.js";import"../../chunk-K56YDHYQ.js";import"../../chunk-XWFH2NHZ.js";import"../../chunk-YRLPFZW3.js";import"../../chunk-BALFHI3G.js";import"../../chunk-X3TYRF5V.js";import"../../chunk-RC25EGHD.js";import"../../chunk-KFV7N2DJ.js";import"../../chunk-23VM4PMV.js";import"../../chunk-B4JN227A.js";import"../../chunk-2XMHLXMA.js";import"../../chunk-G3GWAP5R.js";import"../../chunk-WRFD4EXD.js";import"../../chunk-36LVP5WD.js";import"../../chunk-MFIRYGHW.js";import"../../chunk-67GNT6PF.js";import"../../chunk-P2VEMZH5.js";import"../../chunk-X4T6JT3M.js";import"../../chunk-KRXQM65X.js";import"../../chunk-NKB3KVAB.js";
1
+ export{AddBlockRow as default}from"../../chunk-CM732ENS.js";import"../../chunk-M2Z775ZC.js";import"../../chunk-J4MHNAED.js";import"../../chunk-LNHT2KPX.js";import"../../chunk-U3JP4VGF.js";import"../../chunk-23DYNW6S.js";import"../../chunk-PLVEGYK6.js";import"../../chunk-CPFDJMFD.js";import"../../chunk-TBY3DFKS.js";import"../../chunk-5ZBV5RA6.js";import"../../chunk-BPXEFVSQ.js";import"../../chunk-A6Z4SD3I.js";import"../../chunk-K56YDHYQ.js";import"../../chunk-XWFH2NHZ.js";import"../../chunk-YRLPFZW3.js";import"../../chunk-BALFHI3G.js";import"../../chunk-X3TYRF5V.js";import"../../chunk-RC25EGHD.js";import"../../chunk-KFV7N2DJ.js";import"../../chunk-23VM4PMV.js";import"../../chunk-KRL3IUV3.js";import"../../chunk-2XMHLXMA.js";import"../../chunk-G3GWAP5R.js";import"../../chunk-WRFD4EXD.js";import"../../chunk-36LVP5WD.js";import"../../chunk-MFIRYGHW.js";import"../../chunk-67GNT6PF.js";import"../../chunk-P2VEMZH5.js";import"../../chunk-X4T6JT3M.js";import"../../chunk-KRXQM65X.js";import"../../chunk-NKB3KVAB.js";
@@ -1 +1 @@
1
- export{blockKey,AddSidePanel as default,sectionKey,sharedKey,templateKey}from"../../chunk-2ZWA6ARJ.js";import"../../chunk-PA3PNTVV.js";import"../../chunk-5ZBY24K7.js";import"../../chunk-BVN33R76.js";import"../../chunk-43AEUUNQ.js";import"../../chunk-XDZHKYQX.js";import"../../chunk-UMDQ6CLQ.js";import"../../chunk-M2Z775ZC.js";import"../../chunk-W6SDWCOL.js";import"../../chunk-KCOBHPN6.js";import"../../chunk-R6WTQWAC.js";import"../../chunk-O7Q4DDJT.js";import"../../chunk-RSCMJIWS.js";import"../../chunk-U3JP4VGF.js";import"../../chunk-C2RHV42O.js";import"../../chunk-PLVEGYK6.js";import"../../chunk-QEAUF632.js";import"../../chunk-TBY3DFKS.js";import"../../chunk-5ZBV5RA6.js";import"../../chunk-BPXEFVSQ.js";import"../../chunk-A6Z4SD3I.js";import"../../chunk-K56YDHYQ.js";import"../../chunk-XWFH2NHZ.js";import"../../chunk-YRLPFZW3.js";import"../../chunk-WXFZD7WC.js";import"../../chunk-BALFHI3G.js";import"../../chunk-X3TYRF5V.js";import"../../chunk-RC25EGHD.js";import"../../chunk-KFV7N2DJ.js";import"../../chunk-23VM4PMV.js";import"../../chunk-B4JN227A.js";import"../../chunk-2XMHLXMA.js";import"../../chunk-G3GWAP5R.js";import"../../chunk-WRFD4EXD.js";import"../../chunk-36LVP5WD.js";import"../../chunk-GWIVPLOX.js";import"../../chunk-MJM6SFD3.js";import"../../chunk-Z6PVRXVY.js";import"../../chunk-2OAZYSWJ.js";import"../../chunk-THWO5YVZ.js";import"../../chunk-RE4LP46F.js";import"../../chunk-MFIRYGHW.js";import"../../chunk-LLDHIBYG.js";import"../../chunk-KAML3KQH.js";import"../../chunk-67GNT6PF.js";import"../../chunk-P2VEMZH5.js";import"../../chunk-X4T6JT3M.js";import"../../chunk-KRXQM65X.js";import"../../chunk-NKB3KVAB.js";
1
+ export{blockKey,AddSidePanel as default,sectionKey,sharedKey,templateKey}from"../../chunk-324PHMLC.js";import"../../chunk-PA3PNTVV.js";import"../../chunk-5ZBY24K7.js";import"../../chunk-BVN33R76.js";import"../../chunk-43AEUUNQ.js";import"../../chunk-SINAM2IT.js";import"../../chunk-UMDQ6CLQ.js";import"../../chunk-M2Z775ZC.js";import"../../chunk-J4MHNAED.js";import"../../chunk-KCOBHPN6.js";import"../../chunk-LNHT2KPX.js";import"../../chunk-O7Q4DDJT.js";import"../../chunk-RSCMJIWS.js";import"../../chunk-U3JP4VGF.js";import"../../chunk-23DYNW6S.js";import"../../chunk-PLVEGYK6.js";import"../../chunk-CPFDJMFD.js";import"../../chunk-TBY3DFKS.js";import"../../chunk-5ZBV5RA6.js";import"../../chunk-BPXEFVSQ.js";import"../../chunk-A6Z4SD3I.js";import"../../chunk-K56YDHYQ.js";import"../../chunk-XWFH2NHZ.js";import"../../chunk-YRLPFZW3.js";import"../../chunk-WXFZD7WC.js";import"../../chunk-BALFHI3G.js";import"../../chunk-X3TYRF5V.js";import"../../chunk-RC25EGHD.js";import"../../chunk-KFV7N2DJ.js";import"../../chunk-ZG6PVMC3.js";import"../../chunk-MJM6SFD3.js";import"../../chunk-AJSDWWJW.js";import"../../chunk-2OAZYSWJ.js";import"../../chunk-THWO5YVZ.js";import"../../chunk-RE4LP46F.js";import"../../chunk-23VM4PMV.js";import"../../chunk-KRL3IUV3.js";import"../../chunk-2XMHLXMA.js";import"../../chunk-G3GWAP5R.js";import"../../chunk-WRFD4EXD.js";import"../../chunk-36LVP5WD.js";import"../../chunk-MFIRYGHW.js";import"../../chunk-Z2V3TTAR.js";import"../../chunk-KAML3KQH.js";import"../../chunk-67GNT6PF.js";import"../../chunk-P2VEMZH5.js";import"../../chunk-X4T6JT3M.js";import"../../chunk-KRXQM65X.js";import"../../chunk-NKB3KVAB.js";
@@ -0,0 +1,21 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * THE EDITOR'S MEDIA POPUP — the host's one Media Library, drawn ONCE at the
5
+ * screen's root (harness UI-20, owner decision Lewis 2026-10-23: the library
6
+ * in the left panel was too small; it pops up).
7
+ *
8
+ * The host's component is drawn always and is told what to be: choosing for a
9
+ * field (`pick`), browsing from the rail (`browsing`), or closed (neither). It
10
+ * draws itself as the popup — the shell's `Dialog`, the one the host's forms
11
+ * choose an image in too — so both editors and the forms share one popup and
12
+ * one library. A pick answers the field it was asked for; `onClose` (Escape,
13
+ * the scrim, the ✕, a file dropped on the canvas) leaves the field as it was
14
+ * and ends the browse.
15
+ *
16
+ * Nothing is drawn where the host supplied no popup: fields then ask the host
17
+ * for its window (lib/media-picker).
18
+ */
19
+ declare function MediaPopup(): react.JSX.Element | null;
20
+
21
+ export { MediaPopup as default };
@@ -0,0 +1 @@
1
+ export{MediaPopup as default}from"../../chunk-MS2U6MV5.js";import"../../chunk-LNHT2KPX.js";import"../../chunk-U3JP4VGF.js";