efficient-easel 0.99.2 → 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 (107) hide show
  1. package/README.md +233 -17
  2. package/dist/autosave/index.d.ts +11 -5
  3. package/dist/blocks/index.d.ts +1 -1
  4. package/dist/canvas/index.d.ts +2 -2
  5. package/dist/canvas/index.js +1 -1
  6. package/dist/chrome/chrome.css +96 -29
  7. package/dist/chrome/index.d.ts +67 -18
  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-JUQ6YJCG.js +1 -0
  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-WZ7TGAAT.js +3 -0
  29. package/dist/chunk-ZJEBF4NB.js +1 -0
  30. package/dist/copy/copy.css +388 -5
  31. package/dist/copy/index.d.ts +268 -5
  32. package/dist/copy/index.js +31 -10
  33. package/dist/drag/drag.css +10 -1
  34. package/dist/drag/index.d.ts +99 -8
  35. package/dist/drag/index.js +18 -17
  36. package/dist/easel/easel.css +15 -0
  37. package/dist/easel/index.d.ts +104 -33
  38. package/dist/easel/index.js +9 -1
  39. package/dist/edit-lock/index.d.ts +1 -3
  40. package/dist/edit-lock/index.js +1 -1
  41. package/dist/fields/fields.css +103 -26
  42. package/dist/fields/index.d.ts +47 -9
  43. package/dist/fields/index.js +30 -46
  44. package/dist/gold/index.js +1 -1
  45. package/dist/icons/index.d.ts +10 -1
  46. package/dist/icons/index.js +1 -1
  47. package/dist/index.d.ts +2 -2
  48. package/dist/index.js +1 -1
  49. package/dist/markup/index.d.ts +94 -10
  50. package/dist/markup/index.js +1 -1
  51. package/dist/menu/index.d.ts +58 -5
  52. package/dist/menu/index.js +15 -15
  53. package/dist/menu/menu.css +17 -6
  54. package/dist/node-ops/index.d.ts +1 -1
  55. package/dist/node-ops/index.js +1 -1
  56. package/dist/panel/index.d.ts +72 -4
  57. package/dist/panel/index.js +18 -16
  58. package/dist/panel/panel.css +56 -1
  59. package/dist/pick/index.d.ts +39 -1
  60. package/dist/pick/index.js +7 -14
  61. package/dist/pick/pick.css +14 -0
  62. package/dist/rail/index.d.ts +3 -7
  63. package/dist/rail/index.js +1 -1
  64. package/dist/strings/catalogue.d.ts +13 -35
  65. package/dist/strings/catalogue.js +1 -1
  66. package/dist/strings/index.d.ts +12 -18
  67. package/dist/strings/index.js +2 -2
  68. package/dist/style-clipboard/index.d.ts +1 -1
  69. package/dist/style-clipboard/index.js +1 -1
  70. package/dist/text-styles/index.d.ts +328 -0
  71. package/dist/text-styles/index.js +38 -0
  72. package/dist/text-styles/text-styles.css +309 -0
  73. package/dist/tree/index.d.ts +43 -2
  74. package/dist/tree/index.js +1 -1
  75. package/dist/tree/tree.css +71 -0
  76. package/dist/{types-p9j2Fac7.d.ts → types-DGcCsxs8.d.ts} +72 -7
  77. package/dist/{types-Bs_gVkVI.d.ts → types-DWS-s9z-.d.ts} +1 -9
  78. package/dist/{types-DO-b0LNj.d.ts → types-Djdq6Xkd.d.ts} +9 -8
  79. package/dist/{types-BOSGNvil.d.ts → types-E3HrTPg2.d.ts} +7 -13
  80. package/dist/{types-C7HFvdtk.d.ts → types-VqL6t5HH.d.ts} +82 -5
  81. package/dist/{watch-B5RTR-WX.d.ts → watch-BTYOJpQU.d.ts} +18 -1
  82. package/dist/{words-DagJdwIA.d.ts → words-AHHfiaRY.d.ts} +31 -2
  83. package/dist/{words-c-3h9xQQ.d.ts → words-BMzV04b1.d.ts} +1 -1
  84. package/dist/words-CJjUKcV5.d.ts +37 -0
  85. package/dist/words-CRYvAiHX.d.ts +105 -0
  86. package/dist/{words-CD3WADyb.d.ts → words-CzoyDNHm.d.ts} +4 -0
  87. package/dist/{words-C74EUpFr.d.ts → words-DBq7vfHy.d.ts} +57 -4
  88. package/dist/{words-N4ebT88_.d.ts → words-DgHcY5qB.d.ts} +9 -3
  89. package/package.json +8 -8
  90. package/dist/chunk-66CKSTXP.js +0 -14
  91. package/dist/chunk-672FAEKU.js +0 -1
  92. package/dist/chunk-67VZRQLG.js +0 -13
  93. package/dist/chunk-6PS6STGA.js +0 -1
  94. package/dist/chunk-7A7O2JWQ.js +0 -1
  95. package/dist/chunk-7JTL3P6P.js +0 -1
  96. package/dist/chunk-F2PIG3AA.js +0 -10
  97. package/dist/chunk-H4B72XCJ.js +0 -1
  98. package/dist/chunk-KDIVA6WD.js +0 -1
  99. package/dist/chunk-MCLBHENY.js +0 -1
  100. package/dist/chunk-QB3BZIRA.js +0 -5
  101. package/dist/chunk-SJQG5ENJ.js +0 -1
  102. package/dist/chunk-SUO5627P.js +0 -1
  103. package/dist/chunk-XUE5O62R.js +0 -3
  104. package/dist/chunk-ZT7M24XD.js +0 -1
  105. package/dist/editor/index.d.ts +0 -10
  106. package/dist/editor/index.js +0 -1
  107. /package/dist/{chunk-2PHD4L6A.js → chunk-OWBO4WKA.js} +0 -0
@@ -0,0 +1,309 @@
1
+ /*
2
+ * efficient-easel/text-styles.css — the text-styles list (plan step E6).
3
+ *
4
+ * Email styles → Text and Site styles → Text, drawn once. The numbers are the
5
+ * design's (both pages, final 2026-10-06) and every gap is one of the six
6
+ * spacing tokens (2/4/8/12/16/32, owner ruling 2026-10-02):
7
+ *
8
+ * a row 12 above and below; name → grey line 4
9
+ * an open style its settings 12 apart, name → control 4 (the slider
10
+ * row's own), 16 below the last; indented to line up
11
+ * with the name (the 12 px chevron + 8), none under 260 px
12
+ * group → group 32; group heading → first row 4; "+ Add" 12 above
13
+ *
14
+ * No divider line between rows: this package draws none (card 66350,
15
+ * tests/panel-no-divider-lines.test.ts). The design's hairline under each
16
+ * row is the one thing on the page not drawn here; the 12 px padding
17
+ * separates them.
18
+ *
19
+ * Builds on fields.css (the sliders, the dropdown) and pick.css (the colour
20
+ * window). Light only, like the rest of the editor.
21
+ */
22
+
23
+ .es-ts {
24
+ container-type: inline-size;
25
+ display: flex;
26
+ flex-direction: column;
27
+ min-width: 0;
28
+ }
29
+
30
+ .es-ts-section {
31
+ display: flex;
32
+ flex-direction: column;
33
+ min-width: 0;
34
+ }
35
+ .es-ts-section + .es-ts-section { margin-top: var(--es-space-32, 32px); }
36
+
37
+ .es-ts-group {
38
+ margin: 0 0 var(--es-space-4, 4px);
39
+ font-size: 11px;
40
+ font-weight: 600;
41
+ letter-spacing: 0.06em;
42
+ text-transform: uppercase;
43
+ color: var(--es-muted, #6b7280);
44
+ }
45
+
46
+ /* ── a style's row: the chevron, its name in its own face, the grey line ── */
47
+
48
+ /* The row and, over its end, its actions: Delete on an added style
49
+ (`onRemove`), then the host's own (`rowActions`). As the Layers row's
50
+ (tree.css `.es-tree-actions`): they LIE OVER the end of the row and hold
51
+ no width, so a row with Delete keeps the same room for its name as one
52
+ without; they show on hover AND on focus, over a fade the grey line
53
+ dissolves into. They sit beside the button that opens the row, never
54
+ inside it (a button may not hold a button). */
55
+ .es-ts-item {
56
+ position: relative;
57
+ min-width: 0;
58
+ }
59
+ .es-ts-actions {
60
+ position: absolute;
61
+ right: 0;
62
+ top: 0;
63
+ bottom: 0;
64
+ display: flex;
65
+ align-items: center;
66
+ gap: var(--es-space-2, 2px);
67
+ padding-left: var(--es-space-16, 16px);
68
+ background: linear-gradient(to right, transparent 0, var(--es-panel, #ffffff) 16px);
69
+ opacity: 0;
70
+ pointer-events: none;
71
+ transition: opacity 0.12s;
72
+ }
73
+ .es-ts-item:hover .es-ts-actions,
74
+ .es-ts-item:focus-within .es-ts-actions {
75
+ opacity: 1;
76
+ }
77
+ /* The strip never takes the pointer; only the buttons in it do, once shown,
78
+ so a press on the faded name still opens the row. */
79
+ .es-ts-item:hover .es-ts-actions > *,
80
+ .es-ts-item:focus-within .es-ts-actions > * {
81
+ pointer-events: auto;
82
+ }
83
+ @media (prefers-reduced-motion: reduce) { .es-ts-actions { transition: none; } }
84
+
85
+ /* Delete: the Layers trash (tree.css `.es-tree-del`) — 22 px, the glyph 14,
86
+ red at 0.7, full with a red tint under the pointer. */
87
+ .es-ts-del {
88
+ all: unset;
89
+ box-sizing: border-box;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ width: 22px;
94
+ height: 22px;
95
+ border-radius: 5px;
96
+ color: var(--es-danger, #dc2626);
97
+ opacity: 0.7;
98
+ cursor: pointer;
99
+ transition: opacity 0.12s, background 0.12s, color 0.12s;
100
+ }
101
+ .es-ts-del svg { width: 14px; height: 14px; }
102
+ .es-ts-del:hover,
103
+ .es-ts-del:focus-visible {
104
+ opacity: 1;
105
+ background: var(--es-danger-tint, #fef2f2);
106
+ color: var(--es-danger-strong, #b91c1c);
107
+ }
108
+ .es-ts-del:focus-visible { outline: 2px solid var(--es-accent, #2563eb); outline-offset: 0; }
109
+ @media (prefers-reduced-motion: reduce) { .es-ts-del { transition: none; } }
110
+
111
+ .es-ts-row {
112
+ all: unset;
113
+ box-sizing: border-box;
114
+ display: grid;
115
+ grid-template-columns: 12px minmax(0, 1fr);
116
+ column-gap: var(--es-space-8, 8px);
117
+ row-gap: var(--es-space-4, 4px);
118
+ align-items: center;
119
+ width: 100%;
120
+ padding: var(--es-space-12, 12px) 0;
121
+ cursor: pointer;
122
+ }
123
+ .es-ts-row:focus-visible { outline: 2px solid var(--es-accent, #2563eb); outline-offset: 2px; border-radius: 4px; }
124
+ .es-ts-row[aria-expanded='true'] { padding-bottom: var(--es-space-8, 8px); }
125
+
126
+ .es-ts-chev {
127
+ display: flex;
128
+ grid-row: 1;
129
+ width: 12px;
130
+ height: 12px;
131
+ color: var(--es-field-placeholder, #9ca3af);
132
+ transition: transform 0.12s ease;
133
+ }
134
+ .es-ts-row[aria-expanded='true'] .es-ts-chev { transform: rotate(90deg); }
135
+ @media (prefers-reduced-motion: reduce) { .es-ts-chev { transition: none; } }
136
+
137
+ .es-ts-name {
138
+ grid-column: 2;
139
+ min-width: 0;
140
+ overflow: hidden;
141
+ text-overflow: ellipsis;
142
+ white-space: nowrap;
143
+ line-height: 1.2;
144
+ color: var(--es-text, #111827);
145
+ }
146
+ .es-ts-meta {
147
+ grid-column: 2;
148
+ grid-row: 2;
149
+ min-width: 0;
150
+ overflow: hidden;
151
+ text-overflow: ellipsis;
152
+ white-space: nowrap;
153
+ font-size: 12px;
154
+ line-height: 1.4;
155
+ color: var(--es-muted, #6b7280);
156
+ }
157
+
158
+ /* ── an open style ────────────────────────────────────────────────────────── */
159
+
160
+ .es-ts-open {
161
+ display: flex;
162
+ flex-direction: column;
163
+ gap: var(--es-space-12, 12px);
164
+ min-width: 0;
165
+ padding: var(--es-space-4, 4px) 0 var(--es-space-16, 16px) calc(var(--es-space-12, 12px) + var(--es-space-8, 8px));
166
+ }
167
+ @container (width < 260px) {
168
+ .es-ts-open { padding-left: 0; }
169
+ }
170
+
171
+ /* One line in the style itself; a long one fades out on the right instead of
172
+ making the panel taller. */
173
+ .es-ts-preview {
174
+ box-sizing: border-box;
175
+ overflow: hidden;
176
+ padding: var(--es-space-12, 12px);
177
+ border: 1px solid var(--es-line, #e5e7eb);
178
+ border-radius: var(--es-row-radius, 8px);
179
+ background: var(--es-panel, #ffffff);
180
+ color: var(--es-text, #111827);
181
+ white-space: nowrap;
182
+ -webkit-mask-image: linear-gradient(to right, #000 85%, transparent);
183
+ mask-image: linear-gradient(to right, #000 85%, transparent);
184
+ }
185
+
186
+ .es-ts-set {
187
+ display: flex;
188
+ flex-direction: column;
189
+ gap: var(--es-space-4, 4px);
190
+ min-width: 0;
191
+ }
192
+ /* A setting's name: the slider row's name, so every name in the list is one. */
193
+ .es-ts-sub {
194
+ min-width: 0;
195
+ overflow: hidden;
196
+ text-overflow: ellipsis;
197
+ white-space: nowrap;
198
+ font-size: 12px;
199
+ font-weight: 500;
200
+ color: var(--es-text-secondary, #374151);
201
+ }
202
+ .es-ts-top {
203
+ display: flex;
204
+ align-items: baseline;
205
+ justify-content: space-between;
206
+ gap: var(--es-space-8, 8px);
207
+ min-width: 0;
208
+ }
209
+ .es-ts-colour-read {
210
+ flex: none;
211
+ font: 500 12px ui-monospace, SFMono-Regular, Menlo, monospace;
212
+ color: var(--es-muted, #6b7280);
213
+ }
214
+
215
+ /* The theme's colours in a row, then Any color — the colour window's swatches. */
216
+ .es-ts-swatches {
217
+ display: flex;
218
+ flex-wrap: wrap;
219
+ gap: var(--es-space-8, 8px);
220
+ }
221
+ /* Under 260 px the step down the scale keeps a six-colour theme and Any color
222
+ on one line (7 × 22 + 6 × 4 = 178 of the 196 px a 220 px panel has). */
223
+ @container (width < 260px) {
224
+ .es-ts-swatches { gap: var(--es-space-4, 4px); }
225
+ }
226
+ .es-ts-swatch {
227
+ box-sizing: border-box;
228
+ width: 22px;
229
+ height: 22px;
230
+ padding: 0;
231
+ border: 0;
232
+ border-radius: 6px;
233
+ box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.1);
234
+ cursor: pointer;
235
+ }
236
+ .es-ts-swatch[aria-pressed='true'] {
237
+ box-shadow:
238
+ inset 0 0 0 1px rgb(0 0 0 / 0.1),
239
+ 0 0 0 2px var(--es-panel, #ffffff),
240
+ 0 0 0 4px var(--es-accent, #2563eb);
241
+ }
242
+ .es-ts-swatch:focus-visible { outline: 2px solid var(--es-accent, #2563eb); outline-offset: 2px; }
243
+ .es-ts-swatch:disabled { cursor: default; opacity: 0.6; }
244
+ .es-ts-swatch--any { background: conic-gradient(#f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }
245
+
246
+ /* B · I and Letter case: the text box's 32 px buttons (react-os-shell
247
+ IconButton md); pressed = the light blue the toolbar's pressed marks wear. */
248
+ .es-ts-icons {
249
+ display: flex;
250
+ flex-wrap: wrap;
251
+ gap: var(--es-space-2, 2px);
252
+ }
253
+ .es-ts-icon {
254
+ box-sizing: border-box;
255
+ display: inline-flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ width: 32px;
259
+ height: 32px;
260
+ padding: 0;
261
+ border: 0;
262
+ border-radius: 6px;
263
+ background: transparent;
264
+ color: var(--es-text-secondary, #374151);
265
+ font: inherit;
266
+ cursor: pointer;
267
+ }
268
+ .es-ts-icon--word {
269
+ width: auto;
270
+ padding: 0 var(--es-space-8, 8px);
271
+ font-size: 13px;
272
+ font-weight: 500;
273
+ }
274
+ .es-ts-icon:hover:not(:disabled) { background: var(--es-hover, #f3f4f6); color: var(--es-text, #111827); }
275
+ .es-ts-icon[aria-pressed='true'],
276
+ .es-ts-icon[aria-checked='true'],
277
+ .es-ts-icon[aria-pressed='true']:hover:not(:disabled),
278
+ .es-ts-icon[aria-checked='true']:hover:not(:disabled) {
279
+ background: var(--es-accent-tint, #eff6ff);
280
+ color: var(--es-accent-text, #1d4ed8);
281
+ }
282
+ .es-ts-icon:focus-visible { outline: 2px solid var(--es-accent, #2563eb); outline-offset: 1px; }
283
+ .es-ts-icon:disabled { opacity: 0.6; cursor: not-allowed; }
284
+
285
+ /* The window Any color opens: under the row, in the panel's flow. */
286
+ .es-ts-set > .es-pick-window { margin-top: var(--es-space-4, 4px); }
287
+
288
+ /* ── "+ Add heading" · 3 / 7 ──────────────────────────────────────────────── */
289
+
290
+ .es-ts-add {
291
+ all: unset;
292
+ box-sizing: border-box;
293
+ display: flex;
294
+ align-items: center;
295
+ justify-content: space-between;
296
+ gap: var(--es-space-8, 8px);
297
+ padding: var(--es-space-12, 12px) 0 0;
298
+ font-size: 13px;
299
+ font-weight: 500;
300
+ color: var(--es-accent, #2563eb);
301
+ cursor: pointer;
302
+ }
303
+ .es-ts-add:focus-visible { outline: 2px solid var(--es-accent, #2563eb); outline-offset: 2px; border-radius: 4px; }
304
+ .es-ts-add:disabled { color: var(--es-disabled, #9ca3af); cursor: not-allowed; }
305
+ .es-ts-count {
306
+ font-size: 12px;
307
+ font-weight: 400;
308
+ color: var(--es-field-placeholder, #9ca3af);
309
+ }
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, HTMLAttributes, ButtonHTMLAttributes } from 'react';
3
- import { T as TreeWords } from '../words-N4ebT88_.js';
3
+ import { T as TreeWords } from '../words-DgHcY5qB.js';
4
4
 
5
5
  type LayersTreeProps = {
6
6
  /** Extra classes — a host's scroll-bar class, for instance. */
@@ -111,6 +111,25 @@ type TreeRowActionProps = {
111
111
  onClick: () => void;
112
112
  children: ReactNode;
113
113
  };
114
+ type TreeEmptySlotProps = {
115
+ /** A drag is over the slot and may land in it: it lights up and says "Drop here". */
116
+ drop?: boolean;
117
+ /** Present: the box is a button that adds here (it opens the Add panel at this place). */
118
+ onAdd?: () => void;
119
+ /**
120
+ * What the box says at rest. Default `words.addBlock` ("Add a block") with
121
+ * `onAdd`, `words.emptySlot` ("No blocks yet") without.
122
+ */
123
+ label?: string;
124
+ /**
125
+ * The drag handlers — `efficient-easel/drag`'s
126
+ * `seam({ place: { ...list, index: 0 } }).seamProps`: an empty list has one
127
+ * place, its start.
128
+ */
129
+ seamProps?: HTMLAttributes<HTMLDivElement>;
130
+ /** The box's own words in the host's language: `useEaselStrings().tree`. Left out, English. */
131
+ words?: TreeWords;
132
+ };
114
133
  type TreeSeamProps = {
115
134
  /** A drag is over this gap and may land in it: draw the drop line. */
116
135
  drop?: boolean;
@@ -167,7 +186,29 @@ declare function TreeRowAction({ kind, label, onClick, pressed, disabled, childr
167
186
  * The gap between two rows, as a place (0.74.0): a drop between them lands here,
168
187
  * and — when the host offers it — a "+" adds here. It takes exactly the space
169
188
  * the gap already had, so a list reads the same with seams or without.
189
+ *
190
+ * ⚠️ NOT FOR AN EMPTY LIST. A seam hands its height back to the rows either
191
+ * side of it; alone in a list there are none, and what is left is a strip a
192
+ * few pixels tall with nothing drawn in it. Draw `TreeEmptySlot` there.
170
193
  */
171
194
  declare function TreeSeam({ drop, onAdd, label, seamProps }: TreeSeamProps): react.JSX.Element;
195
+ /**
196
+ * AN EMPTY LIST, AS A PLACE (second host, 2026-10-06): where a slot holds
197
+ * nothing yet — a column with no blocks — a dashed box the size of a row. A
198
+ * drop on it lands in the slot, and when the host offers it the box is a
199
+ * button that adds there. While a drag may land it lights up and says so.
200
+ *
201
+ * Until then an empty list was drawn as its one seam, and a seam is a gap: a
202
+ * drop aimed at "into this empty column" missed its few pixels and landed on
203
+ * whatever row or seam was really under the pointer. The canvas has always
204
+ * drawn the same idea as its "+ Add block" cell (`efficient-easel/drag`).
205
+ *
206
+ * Lifted from the campaign designer's `.cd-tree-add` (efficient-admin-portal
207
+ * `EmailOutline`), which had drawn one of its own. Hook-free and controlled,
208
+ * like every part of the tree: the drag is `efficient-easel/drag`'s —
209
+ *
210
+ * <TreeEmptySlot {...drag.seam({ place: { ...list, index: 0 } })} onAdd={…} />
211
+ */
212
+ declare function TreeEmptySlot({ drop, onAdd, label, seamProps, words }: TreeEmptySlotProps): react.JSX.Element;
172
213
 
173
- export { LayersTree, type LayersTreeProps, Subtree, type SubtreeProps, TREE_DRAG_HINT, TreeGroupLabel, type TreeGroupLabelProps, TreeRow, TreeRowAction, type TreeRowActionProps, type TreeRowFold, type TreeRowMark, type TreeRowProps, TreeSeam, type TreeSeamProps, TreeWords };
214
+ export { LayersTree, type LayersTreeProps, Subtree, type SubtreeProps, TREE_DRAG_HINT, TreeEmptySlot, type TreeEmptySlotProps, TreeGroupLabel, type TreeGroupLabelProps, TreeRow, TreeRowAction, type TreeRowActionProps, type TreeRowFold, type TreeRowMark, type TreeRowProps, TreeSeam, type TreeSeamProps, TreeWords };
@@ -1 +1 @@
1
- export{LayersTree,Subtree,TREE_DRAG_HINT,TreeGroupLabel,TreeRow,TreeRowAction,TreeSeam}from"../chunk-66CKSTXP.js";import"../chunk-H4B72XCJ.js";import"../chunk-PI26YXYD.js";
1
+ export{LayersTree,Subtree,TREE_DRAG_HINT,TreeEmptySlot,TreeGroupLabel,TreeRow,TreeRowAction,TreeSeam}from"../chunk-V7GIIXFB.js";import"../chunk-3EDSDPIP.js";import"../chunk-6EIVTKZZ.js";
@@ -616,3 +616,74 @@
616
616
  .es-subtree > .es-tree-seam:last-child {
617
617
  margin-bottom: calc(-1 * var(--es-rhythm-between));
618
618
  }
619
+
620
+ /* ── An empty list, as a place (second host, 2026-10-06) ───────────────────
621
+ Where a slot holds nothing yet: a box the size of a row, that takes a drop
622
+ and — when the host offers it — adds there. NOT a seam: a seam is the gap
623
+ between two rows and takes back the space it adds (above), so alone in a
624
+ list it was a strip 8px tall with nothing drawn in it, and a drop aimed at
625
+ "into this empty column" landed on whatever was really under the pointer.
626
+
627
+ Dashed, because the thing is not there yet — the canvas says the same with
628
+ its "+ Add block" cell (`.es-canvas-empty`, drag.css). Read off the campaign
629
+ designer's `.cd-tree-add` (efficient-admin-portal), which drew one of its
630
+ own; 34px is a row's height (8 + 18 + 8). */
631
+ .es-tree-empty {
632
+ box-sizing: border-box;
633
+ flex: none;
634
+ min-height: 34px;
635
+ display: flex;
636
+ align-items: stretch;
637
+ border: 1px dashed var(--es-line);
638
+ border-radius: var(--es-row-radius);
639
+ color: var(--es-text-secondary);
640
+ font: 500 12px/1.3 var(--es-font);
641
+ transition: border-color 0.12s, color 0.12s, background 0.12s;
642
+ }
643
+ .es-tree-empty-add,
644
+ .es-tree-empty-note {
645
+ flex: 1;
646
+ min-width: 0;
647
+ display: flex;
648
+ align-items: center;
649
+ gap: 8px;
650
+ padding: 0 12px;
651
+ }
652
+ /* The whole box is the button. A bare one: a native button ignores an author's
653
+ background and border on some platforms, so the box draws them. */
654
+ .es-tree-empty-add {
655
+ border: none;
656
+ border-radius: inherit;
657
+ background: none;
658
+ color: inherit;
659
+ font: inherit;
660
+ text-align: left;
661
+ cursor: pointer;
662
+ }
663
+ .es-tree-empty-add:focus-visible {
664
+ outline: 2px solid var(--es-accent);
665
+ outline-offset: -2px;
666
+ }
667
+ /* Nothing to do here: the list is empty, and that is all it says. It SAYS it,
668
+ so it is a grey that can be read: it was `--es-disabled` (#9ca3af), 2.5:1 on
669
+ the panel, which axe-core failed on a read-only tree (2026-10-07). Held by
670
+ ratio in tests/contrast-small-text.test.ts. */
671
+ .es-tree-empty-note {
672
+ color: var(--es-muted);
673
+ }
674
+ .es-tree-empty.has-add:hover,
675
+ .es-tree-empty.is-drop {
676
+ border-color: var(--es-accent);
677
+ color: var(--es-accent-text);
678
+ }
679
+ .es-tree-empty.is-drop {
680
+ background: var(--es-accent-tint);
681
+ }
682
+ .es-tree-empty.is-drop .es-tree-empty-note {
683
+ color: inherit;
684
+ }
685
+ /* At the top level rows stack as blocks: keep the row's own distance from one above. */
686
+ .es-tree > .es-tree-row + .es-tree-empty,
687
+ .es-tree > .es-subtree + .es-tree-empty {
688
+ margin-top: var(--es-rhythm-between);
689
+ }
@@ -1,4 +1,4 @@
1
- import { a as EaselRailItem } from './types-Bs_gVkVI.js';
1
+ import { a as EaselRailItem } from './types-DWS-s9z-.js';
2
2
  import { CSSProperties, ReactNode, Ref } from 'react';
3
3
  import { EditAccess, SaveConflict } from './edit-lock/index.js';
4
4
 
@@ -40,9 +40,30 @@ declare function clampPanelWidth(width: number): number;
40
40
  * it. The website editor learned this on 2026-09-04: the vars sat on the panel
41
41
  * row, the top bar is that row's SIBLING, and the rail column read a width with
42
42
  * no declaration in scope — which is not an error, it silently falls back to
43
- * `auto` and the column shrinks to its buttons.
43
+ * `auto` and the column shrinks to its buttons. (Since 2026-10-06 chrome.css
44
+ * falls back to `DEFAULT_PANEL_W` instead: the column keeps its width and
45
+ * simply stops following the grip.)
46
+ *
47
+ * ⚠️ A SERVER RENDERS THIS AS A `style=""` ATTRIBUTE, and a
48
+ * Content-Security-Policy without `'unsafe-inline'` for styles refuses those.
49
+ * A client render is not affected: React writes a `style` prop through the
50
+ * CSSOM. A server-rendered editor under such a policy uses
51
+ * {@link setPanelWidthVars} instead.
44
52
  */
45
53
  declare function panelWidthVars(widths: PanelWidths): CSSProperties;
54
+ /**
55
+ * The same two variables, written onto the editor root through the CSSOM
56
+ * (second host, 2026-10-06) — so the server's HTML carries no `style` attribute for a strict
57
+ * Content-Security-Policy to refuse:
58
+ *
59
+ * <div className="es-editor" ref={(el) => { if (el) setPanelWidthVars(el, widths); }}>
60
+ *
61
+ * A ref runs on the client only, before the browser paints, and again on each
62
+ * render — so it follows the widths as a grip is dragged. Until it runs (the
63
+ * server's HTML, before hydration) chrome.css draws the panels at
64
+ * `DEFAULT_PANEL_W`. Touches nothing but the element it is handed.
65
+ */
66
+ declare function setPanelWidthVars(el: HTMLElement, widths: PanelWidths): void;
46
67
 
47
68
  type TopBarProps = {
48
69
  children?: ReactNode;
@@ -146,12 +167,36 @@ type InspectorHeadProps = {
146
167
  /** `InspectorIconButton`s, right-aligned. Omit when the thing has no actions. */
147
168
  actions?: ReactNode;
148
169
  };
170
+ /** What a field can hold, as `MediaPanelHead` says it (`ChromeWords.mediaKinds`). */
171
+ type MediaPickKind = 'image' | 'video' | 'svg';
172
+ type MediaPanelHeadProps = {
173
+ /** The line under "Media library": which website or campaign the panel is
174
+ * open in. Also the line under "Choose …" when the field has no name. */
175
+ context: ReactNode;
176
+ /** The field choosing right now, or null while browsing. `field` is the
177
+ * host's label for it ("Background image"). */
178
+ pick: {
179
+ kind: MediaPickKind;
180
+ field?: string;
181
+ } | null;
182
+ /** Leave choosing: the field keeps what it had. */
183
+ onCancel: () => void;
184
+ /** Hide the panel. Omitted where the panel cannot be hidden, and then no
185
+ * Hide button is drawn. */
186
+ onHide?: () => void;
187
+ /** Which side panel the head is on; picks the Hide glyph and its name. */
188
+ side?: 'left' | 'right';
189
+ };
149
190
  type InspectorUpLevelProps = {
150
- /** What the level above is called — the section, the column, the group. */
191
+ /** What the level above is called — the section, the column, the group. The link's words. */
151
192
  name: string;
152
193
  /** Select the level above. */
153
194
  onSelect: () => void;
154
- /** The glyph before the name. The host's own until the icon set carries one. */
195
+ /**
196
+ * @deprecated Ignored since the one up-level look (Lewis, 2026-10-06): the
197
+ * link draws its own ← and a host glyph is a second look. Kept so a host
198
+ * that still passes one compiles; drop it from your call.
199
+ */
155
200
  icon?: ReactNode;
156
201
  /**
157
202
  * The tooltip, and the start of the accessible name ("… to ‹name›"). Default
@@ -159,6 +204,12 @@ type InspectorUpLevelProps = {
159
204
  * is `EaselStrings.chrome.upLevelTo`.
160
205
  */
161
206
  label?: string;
207
+ /**
208
+ * Draw the link alone, without the panel-top inset — for a level inside the
209
+ * panel body (an open list item, a sub-panel). Default: the panel-top place,
210
+ * above the inspector head.
211
+ */
212
+ inline?: boolean;
162
213
  };
163
214
  type InspectorIconButtonProps = {
164
215
  /** Accessible name, and the tooltip unless `title` says something else. */
@@ -180,11 +231,27 @@ type InspectorIconButtonProps = {
180
231
  buttonRef?: Ref<HTMLButtonElement>;
181
232
  children: ReactNode;
182
233
  };
234
+ /**
235
+ * Which of its words a top-bar rail is drawing — the settings tabs' three
236
+ * states: `'all'` every button with its word, `'chosen'` the glyphs plus the
237
+ * word of the button whose panel is open, `'none'` glyphs only.
238
+ */
239
+ type RailWordsShown = 'all' | 'chosen' | 'none';
183
240
  type TopBarRailProps = {
184
241
  /** The rail's buttons (`EaselRail` items). */
185
242
  items: EaselRailItem[];
186
243
  /** The toolbar's own name, e.g. "Left panel views". */
187
244
  ariaLabel?: string;
245
+ /**
246
+ * Told which words the rail is drawing, once they are measured and again
247
+ * whenever that changes (the panel dragged, a longer language, another panel
248
+ * opened). The width decides in CSS; this is that answer read back, for a
249
+ * host that has to know — to shorten a word, widen its panel's floor, or
250
+ * note that "Zurück" and "Ebenen" fold where "Back" and "Layers" do not.
251
+ * Every button keeps its name as `title` and `aria-label` in all three.
252
+ * Left out, nothing is observed.
253
+ */
254
+ onWords?: (shown: RailWordsShown) => void;
188
255
  };
189
256
  type EditNoticeProps = {
190
257
  /** Who may write the document now (`efficient-easel/edit-lock`). */
@@ -200,7 +267,5 @@ type EditNoticeProps = {
200
267
  /** Throw this screen away and load what is saved — the only action that discards work. */
201
268
  onReload: () => void;
202
269
  };
203
- /** @deprecated The name before the Easel rename: `EaselIconButtonProps`. */
204
- type EditorIconButtonProps = EaselIconButtonProps;
205
270
 
206
- export { type DeviceSwitchProps as D, type EaselIconButtonProps as E, type InspectorHeadProps as I, MAX_PANEL_W as M, type PanelResizeHandleProps as P, type SaveTone as S, type TopBarProps as T, type TopBarClusterProps as a, type TopBarRailProps as b, type SaveStateProps as c, type SaveStateToggleProps as d, type InspectorIconButtonProps as e, type InspectorUpLevelProps as f, type EditNoticeProps as g, DEFAULT_PANEL_W as h, type DeviceOption as i, type EditorIconButtonProps as j, MIN_PANEL_W as k, type PanelSide as l, type PanelWidths as m, clampPanelWidth as n, panelWidthVars as p };
271
+ export { type DeviceSwitchProps as D, type EaselIconButtonProps as E, type InspectorHeadProps as I, type MediaPanelHeadProps as M, type PanelResizeHandleProps as P, type RailWordsShown as R, type SaveTone as S, type TopBarProps as T, type TopBarClusterProps as a, type TopBarRailProps as b, type SaveStateProps as c, type SaveStateToggleProps as d, type InspectorIconButtonProps as e, type InspectorUpLevelProps as f, type MediaPickKind as g, type EditNoticeProps as h, DEFAULT_PANEL_W as i, type DeviceOption as j, MAX_PANEL_W as k, MIN_PANEL_W as l, type PanelSide as m, type PanelWidths as n, clampPanelWidth as o, panelWidthVars as p, setPanelWidthVars as s };
@@ -84,13 +84,5 @@ interface EaselRailButtonProps {
84
84
  onSelect?: () => void;
85
85
  className?: string;
86
86
  }
87
- /** @deprecated The name before the Easel rename: `EaselRailItem`. */
88
- type EditorRailItem = EaselRailItem;
89
- /** @deprecated The name before the Easel rename: `EaselRailOrientation`. */
90
- type EditorRailOrientation = EaselRailOrientation;
91
- /** @deprecated The name before the Easel rename: `EaselRailProps`. */
92
- type EditorRailProps = EaselRailProps;
93
- /** @deprecated The name before the Easel rename: `EaselRailButtonProps`. */
94
- type EditorRailButtonProps = EaselRailButtonProps;
95
87
 
96
- export type { EaselRailButtonProps as E, EaselRailItem as a, EaselRailOrientation as b, EaselRailProps as c, EditorRailButtonProps as d, EditorRailItem as e, EditorRailOrientation as f, EditorRailProps as g };
88
+ export type { EaselRailButtonProps as E, EaselRailItem as a, EaselRailOrientation as b, EaselRailProps as c };
@@ -118,20 +118,21 @@ interface EaselContextMenuProps {
118
118
  /** Defaults to {@link ES_MENU_SKIN}, the storefront editor's approved look. */
119
119
  skin?: EaselContextMenuSkin;
120
120
  /**
121
- * Where to portal to. Defaults to `document.body`.
121
+ * Where to portal to. Defaults to `document.body` (`null` means the default).
122
122
  *
123
123
  * A host that OVERRIDES `--es-*` on its own editor root should pass that root
124
124
  * instead: the default skin re-declares the token defaults on the box (see the
125
125
  * component's note), which is right for a box hanging off `<body>` and wrong
126
126
  * for one that should inherit a re-themed editor's values.
127
+ *
128
+ * So should a host that wants the box INSIDE ITS LANDMARKS (on the body it
129
+ * is page content outside every landmark: axe-core's `region`) — through
130
+ * `menuPortalTarget(root)`, never the bare root: the box is `position:
131
+ * fixed`, and a transformed, filtered or contained ancestor places and clips
132
+ * it. `menuPortalTarget` answers the root only when none does. The decision
133
+ * is recorded in `./portal`.
127
134
  */
128
135
  portalTo?: HTMLElement | null;
129
136
  }
130
- /** @deprecated The name before the Easel rename: `EaselContextMenuItem`. */
131
- type EditorContextMenuItem = EaselContextMenuItem;
132
- /** @deprecated The name before the Easel rename: `EaselContextMenuSkin`. */
133
- type EditorContextMenuSkin = EaselContextMenuSkin;
134
- /** @deprecated The name before the Easel rename: `EaselContextMenuProps`. */
135
- type EditorContextMenuProps = EaselContextMenuProps;
136
137
 
137
- export type { EaselContextMenuItem as E, EaselContextMenuProps as a, EaselContextMenuSkin as b, EditorContextMenuItem as c, EditorContextMenuProps as d, EditorContextMenuSkin as e };
138
+ export type { EaselContextMenuItem as E, EaselContextMenuProps as a, EaselContextMenuSkin as b };
@@ -34,6 +34,12 @@ type SetDataPatch = Partial<Data> | DataInput;
34
34
  /**
35
35
  * The actions the apps dispatch today — Puck 0.20's, with Puck's meaning. Zone
36
36
  * keys are `"<parentId>:<slot>"`; `root:default-zone` is the page's own list.
37
+ *
38
+ * `insert` puts the block in with its `defaultProps` and runs NO resolver, as
39
+ * Puck's reducer did not. What ran a new block's `resolveData` in Puck was its
40
+ * own drop from the component list; here that is `easel.resolveNode(id, {
41
+ * trigger: 'insert' })`, called by the host after the insert (pass the action
42
+ * an `id` so there is one to name).
37
43
  */
38
44
  type EaselAction = ({
39
45
  type: 'setData';
@@ -149,17 +155,5 @@ interface EaselView<C extends EaselConfig = EaselConfig> {
149
155
  * did, across a data swap, which the website editor wrapped. */
150
156
  getSelectorForId: (id: string) => Required<ItemSelector> | undefined;
151
157
  }
152
- /** @deprecated The name before the Easel rename: `EaselUi`. */
153
- type EditorUi = EaselUi;
154
- /** @deprecated The name before the Easel rename: `EaselAction`. */
155
- type EditorAction = EaselAction;
156
- /** @deprecated The name before the Easel rename: `EaselConfig`. */
157
- type EditorConfig = EaselConfig;
158
- /** @deprecated The name before the Easel rename: `EaselCore`. */
159
- type EditorCore = EaselCore;
160
- /** @deprecated The name before the Easel rename: `EaselHistory`. */
161
- type EditorHistory = EaselHistory;
162
- /** @deprecated The name before the Easel rename: `EaselView`. */
163
- type EditorView<C extends EaselConfig = EaselConfig> = EaselView<C>;
164
158
 
165
- export type { AppState as A, EaselConfig as E, HistoryEntry as H, Permissions as P, SetDataPatch as S, EaselView as a, EaselAction as b, EaselCore as c, EaselUi as d, EaselHistory as e, EditorAction as f, EditorConfig as g, EditorCore as h, EditorHistory as i, EditorUi as j, EditorView as k };
159
+ export type { AppState as A, EaselConfig as E, HistoryEntry as H, Permissions as P, SetDataPatch as S, EaselView as a, EaselAction as b, EaselCore as c, EaselUi as d, EaselHistory as e };