efficient-easel 0.0.0 → 0.96.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 (119) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +723 -4
  3. package/dist/autosave/index.d.ts +101 -0
  4. package/dist/autosave/index.js +1 -0
  5. package/dist/blocks/index.d.ts +295 -0
  6. package/dist/blocks/index.js +0 -0
  7. package/dist/buckets-1vRlQceY.d.ts +13 -0
  8. package/dist/canvas/index.d.ts +194 -0
  9. package/dist/canvas/index.js +1 -0
  10. package/dist/chrome/chrome.css +570 -0
  11. package/dist/chrome/index.d.ts +219 -0
  12. package/dist/chrome/index.js +21 -0
  13. package/dist/chunk-2LUSK32B.js +1 -0
  14. package/dist/chunk-2OAZYSWJ.js +1 -0
  15. package/dist/chunk-67VZRQLG.js +13 -0
  16. package/dist/chunk-6JDII7N3.js +1 -0
  17. package/dist/chunk-6OPSJGAI.js +1 -0
  18. package/dist/chunk-6WY6ZXJG.js +1 -0
  19. package/dist/chunk-7A7O2JWQ.js +1 -0
  20. package/dist/chunk-BZ5ASXOL.js +1 -0
  21. package/dist/chunk-CERTFXCV.js +13 -0
  22. package/dist/chunk-EFTTSIMK.js +1 -0
  23. package/dist/chunk-FI23ALVJ.js +1 -0
  24. package/dist/chunk-FJE4IDBQ.js +1 -0
  25. package/dist/chunk-GGTPNGFJ.js +1 -0
  26. package/dist/chunk-H4B72XCJ.js +1 -0
  27. package/dist/chunk-H6DDIBQ4.js +1 -0
  28. package/dist/chunk-KDIVA6WD.js +1 -0
  29. package/dist/chunk-LPRQDDQO.js +1 -0
  30. package/dist/chunk-MCLBHENY.js +1 -0
  31. package/dist/chunk-MFIRYGHW.js +1 -0
  32. package/dist/chunk-NKB3KVAB.js +1 -0
  33. package/dist/chunk-O2NLQBFX.js +1 -0
  34. package/dist/chunk-OGZFDJ6G.js +1 -0
  35. package/dist/chunk-OWBO4WKA.js +0 -0
  36. package/dist/chunk-QB3BZIRA.js +5 -0
  37. package/dist/chunk-RE4LP46F.js +1 -0
  38. package/dist/chunk-STFMZJKU.js +14 -0
  39. package/dist/chunk-SUO5627P.js +1 -0
  40. package/dist/chunk-TDA4HH54.js +1 -0
  41. package/dist/chunk-WK3AFPXY.js +1 -0
  42. package/dist/chunk-YIMRTIBN.js +3 -0
  43. package/dist/chunk-YNZZYHA4.js +450 -0
  44. package/dist/chunk-ZAOM6PFH.js +1 -0
  45. package/dist/chunk-ZNCEOSXO.js +1 -0
  46. package/dist/copy/copy.css +282 -0
  47. package/dist/copy/index.d.ts +336 -0
  48. package/dist/copy/index.js +10 -0
  49. package/dist/data/index.d.ts +208 -0
  50. package/dist/data/index.js +1 -0
  51. package/dist/drag/drag.css +360 -0
  52. package/dist/drag/index.d.ts +379 -0
  53. package/dist/drag/index.js +18 -0
  54. package/dist/edit-lock/index.d.ts +354 -0
  55. package/dist/edit-lock/index.js +1 -0
  56. package/dist/editor/editor.css +58 -0
  57. package/dist/editor/index.d.ts +361 -0
  58. package/dist/editor/index.js +9 -0
  59. package/dist/fields/fields.css +207 -0
  60. package/dist/fields/index.d.ts +273 -0
  61. package/dist/fields/index.js +46 -0
  62. package/dist/gold/gold.css +196 -0
  63. package/dist/gold/index.d.ts +280 -0
  64. package/dist/gold/index.js +1 -0
  65. package/dist/icons/index.d.ts +474 -0
  66. package/dist/icons/index.js +1 -0
  67. package/dist/index.d.ts +6 -0
  68. package/dist/index.js +1 -0
  69. package/dist/markup/index.d.ts +452 -0
  70. package/dist/markup/index.js +1 -0
  71. package/dist/menu/index.d.ts +49 -0
  72. package/dist/menu/index.js +15 -0
  73. package/dist/menu/menu.css +254 -0
  74. package/dist/node-ops/index.d.ts +91 -0
  75. package/dist/node-ops/index.js +1 -0
  76. package/dist/panel/index.d.ts +1088 -0
  77. package/dist/panel/index.js +16 -0
  78. package/dist/panel/panel.css +1460 -0
  79. package/dist/pick/index.d.ts +153 -0
  80. package/dist/pick/index.js +14 -0
  81. package/dist/pick/pick.css +235 -0
  82. package/dist/plan-DtNw2VuI.d.ts +88 -0
  83. package/dist/rail/index.d.ts +145 -0
  84. package/dist/rail/index.js +1 -0
  85. package/dist/rail/rail.css +175 -0
  86. package/dist/render/index.d.ts +117 -0
  87. package/dist/render/index.js +5 -0
  88. package/dist/resolve-DpH8YQOA.d.ts +57 -0
  89. package/dist/shell/index.d.ts +159 -0
  90. package/dist/shell/index.js +1 -0
  91. package/dist/shell/shell.css +122 -0
  92. package/dist/strings/catalogue.d.ts +141 -0
  93. package/dist/strings/catalogue.js +1 -0
  94. package/dist/strings/index.d.ts +56 -0
  95. package/dist/strings/index.js +2 -0
  96. package/dist/style-clipboard/index.d.ts +184 -0
  97. package/dist/style-clipboard/index.js +1 -0
  98. package/dist/tree/index.d.ts +173 -0
  99. package/dist/tree/index.js +1 -0
  100. package/dist/tree/tree.css +618 -0
  101. package/dist/types-CEqUFH2j.d.ts +129 -0
  102. package/dist/types-CuNiSnX6.d.ts +204 -0
  103. package/dist/types-DNrn_w4V.d.ts +88 -0
  104. package/dist/types-DziKCwsN.d.ts +105 -0
  105. package/dist/types-_Vayx8ov.d.ts +153 -0
  106. package/dist/types-pGBXevBU.d.ts +131 -0
  107. package/dist/useNodeOps-6IAej9jy.d.ts +68 -0
  108. package/dist/watch-DmasQA61.d.ts +60 -0
  109. package/dist/wordFit-9B9nqyf3.d.ts +35 -0
  110. package/dist/words-B2YLaKEr.d.ts +30 -0
  111. package/dist/words-B87GIFWH.d.ts +86 -0
  112. package/dist/words-BK-npta-.d.ts +27 -0
  113. package/dist/words-CD3WADyb.d.ts +51 -0
  114. package/dist/words-CsRWXkY4.d.ts +129 -0
  115. package/dist/words-D9ChvbNC.d.ts +52 -0
  116. package/dist/words-DOhF-4D4.d.ts +35 -0
  117. package/dist/words-DagJdwIA.d.ts +78 -0
  118. package/dist/words-DfxEp_RH.d.ts +142 -0
  119. package/package.json +202 -3
@@ -0,0 +1,618 @@
1
+ /*
2
+ * efficient-easel/tree.css — the Layers tree: one row shape at every depth.
3
+ *
4
+ * EVERY VALUE HERE WAS READ OFF `efficient-shop/components/storefront-editor.css`
5
+ * on origin/main (release 14.24.0, 2026-09-17), where the website editor drew
6
+ * the tree as `.sf-tree` / `.sf-row` / `.sf-subtree` / `.sf-group-label` (the
7
+ * owner's "One Ladder", 2026-09-04). Its `--sf-*` tokens alias the `--es-*`
8
+ * tokens spent here. New with the move: `.es-tree-preview` (0.42.0), and the
9
+ * campaign editor's select button, marks, badge, end mark and plain action (0.44.0).
10
+ *
11
+ * Needs `efficient-easel/shell.css`.
12
+ */
13
+
14
+ .es-tree {
15
+ flex: 1;
16
+ overflow-y: auto;
17
+ /* LEFT 8 — the inset a rounded row needs. RIGHT 11 — the room a real,
18
+ space-taking 11px scrollbar takes, so nothing at a row's end sits under
19
+ the thumb. Words inside start on one 11px column down the panel. */
20
+ padding: 0 11px 10px 8px;
21
+ /* THE TREE'S RHYTHM, under the shell's names, at the tree's own density: the
22
+ owner picked 10 between groups and 6 between rows by eye (2026-08-31). They
23
+ are declared HERE, not on the editor root, so the settings panel beside the
24
+ tree keeps its own values for the same names. */
25
+ --es-rhythm-group: 10px;
26
+ --es-rhythm-between: 6px;
27
+ }
28
+ /* Every seam the tree stacks, spelled out: labels and subtrees interleave with
29
+ rows, and a rule that knew only rows would leave those seams to chance. */
30
+ .es-tree > .es-tree-row + .es-tree-row,
31
+ .es-tree > .es-subtree + .es-tree-row {
32
+ margin-top: var(--es-rhythm-between);
33
+ }
34
+
35
+ .es-tree-group {
36
+ font-size: 10.5px;
37
+ font-weight: 600;
38
+ letter-spacing: 0.07em;
39
+ text-transform: uppercase;
40
+ color: var(--es-muted);
41
+ padding: var(--es-rhythm-group) 11px var(--es-rhythm-between);
42
+ }
43
+
44
+ /* THE ONLY THING IN THE TREE THAT SAYS DEPTH: 16px of indent and a hairline
45
+ that runs the height of the group. Rows inside sit exactly as far apart as
46
+ rows outside, so the ladder reads as one list at several depths. */
47
+ .es-subtree {
48
+ margin: var(--es-rhythm-between) 0 var(--es-rhythm-between) 16px;
49
+ padding-left: 9px;
50
+ border-left: 1px solid var(--es-line);
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: var(--es-rhythm-between);
54
+ }
55
+
56
+ .es-tree-row {
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 8px;
60
+ padding: 7px 11px;
61
+ border-radius: var(--es-row-radius);
62
+ cursor: pointer;
63
+ transition: background 0.12s;
64
+ /* Anchors the drop rules and the out-of-flow drag handle. */
65
+ position: relative;
66
+ /* The ground this row is painted on, as a value its children can read: the
67
+ actions' fade must end in the colour really behind the row. Every rule that
68
+ changes the row's background changes this in the same block. */
69
+ --es-row-scrim: var(--es-panel);
70
+ }
71
+ .es-tree-row:hover {
72
+ --es-row-scrim: var(--es-hover);
73
+ background: var(--es-row-scrim);
74
+ }
75
+ .es-tree-row.is-dragging {
76
+ opacity: 0.4;
77
+ }
78
+ .es-tree-row.is-drop-before::before,
79
+ .es-tree-row.is-drop-after::after {
80
+ content: "";
81
+ position: absolute;
82
+ left: 8px;
83
+ right: 8px;
84
+ height: 2px;
85
+ background: var(--es-accent);
86
+ border-radius: 2px;
87
+ pointer-events: none;
88
+ }
89
+ /* THE ONE DROP LINE (0.74.0): centred in the gap, where a seam draws its own —
90
+ so a drop between two rows looks the same whether the pointer is over the
91
+ lower half of one, the gap, or the upper half of the next. */
92
+ .es-tree-row.is-drop-before::before {
93
+ top: calc(-1px - var(--es-rhythm-between) / 2);
94
+ }
95
+ .es-tree-row.is-drop-after::after {
96
+ bottom: calc(-1px - var(--es-rhythm-between) / 2);
97
+ }
98
+ /* An outline, not a border: a border would move every row below by 2px as the
99
+ pointer crossed, which reads as the list flinching. */
100
+ .es-tree-row.is-drop-into {
101
+ outline: 2px solid var(--es-accent);
102
+ outline-offset: -2px;
103
+ border-radius: 6px;
104
+ background: var(--es-accent-tint);
105
+ }
106
+ .es-tree-row.is-selected {
107
+ --es-row-scrim: var(--es-accent-tint);
108
+ background: var(--es-row-scrim);
109
+ box-shadow: inset 2px 0 0 var(--es-accent);
110
+ }
111
+ .es-tree-row.is-selected .es-tree-chip {
112
+ background: var(--es-raised);
113
+ color: var(--es-accent);
114
+ }
115
+ .es-tree-row.is-selected .es-tree-name {
116
+ color: var(--es-accent-text);
117
+ font-weight: 600;
118
+ }
119
+
120
+ /* No ground and no rule: every row carries a glyph, so the glyph is the mark. */
121
+ .es-tree-chip {
122
+ width: 18px;
123
+ height: 18px;
124
+ border-radius: 5px;
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ font-size: 12px;
129
+ flex: none;
130
+ background: transparent;
131
+ color: var(--es-icon);
132
+ }
133
+ .es-tree-chip svg {
134
+ width: 13px;
135
+ height: 13px;
136
+ }
137
+
138
+ /* The name's box: what the actions are positioned against, so they cover the
139
+ name's tail and never the caret. */
140
+ .es-tree-name-box {
141
+ position: relative;
142
+ flex: 1;
143
+ min-width: 0;
144
+ display: flex;
145
+ align-items: center;
146
+ }
147
+ /* One size at every depth — 12px / 500, the group pill's size at a row's weight. */
148
+ .es-tree-name {
149
+ flex: 1;
150
+ font-size: 12px;
151
+ font-weight: 500;
152
+ color: var(--es-text);
153
+ white-space: nowrap;
154
+ overflow: hidden;
155
+ text-overflow: ellipsis;
156
+ }
157
+ /* NEW in 0.42.0: the thing's own first words after its type — "Text BACK IN
158
+ STOCK NOW" — quieter than the name, and the first thing to be cut when the
159
+ panel is narrow (it sits inside the name's own ellipsis). */
160
+ .es-tree-preview {
161
+ margin-left: 6px;
162
+ font-weight: 400;
163
+ color: var(--es-muted);
164
+ }
165
+ .es-tree-row.is-selected .es-tree-preview {
166
+ color: var(--es-accent-text);
167
+ font-weight: 400;
168
+ }
169
+ .es-tree-row.is-hidden .es-tree-name {
170
+ color: var(--es-muted);
171
+ text-decoration: line-through;
172
+ }
173
+
174
+ .es-tree-actions {
175
+ position: absolute;
176
+ right: 0;
177
+ top: 0;
178
+ bottom: 0;
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 2px;
182
+ /* 22px of fade so the name dissolves under the buttons instead of being
183
+ chopped, ending in the ground this row is really painted on. */
184
+ padding-left: 22px;
185
+ background: linear-gradient(to right, transparent 0, var(--es-row-scrim) 22px);
186
+ opacity: 0;
187
+ pointer-events: none;
188
+ transition: opacity 0.12s;
189
+ }
190
+ /* Hover AND focus. Bound to hover alone, a keyboard user who tabs to the trash
191
+ gets it painted onto the name with nothing behind it. */
192
+ .es-tree-row:hover .es-tree-actions,
193
+ .es-tree-row:focus-within .es-tree-actions {
194
+ opacity: 1;
195
+ }
196
+ /* THE STRIP NEVER TAKES THE POINTER — only the buttons in it do, and each arms
197
+ its own as it shows (0.44.1). On a short row the fade covers most of the name,
198
+ and a row that selects from its NAME button (`onSelect`) must still get the
199
+ click: with the strip taking it, a hovered row could not be selected at all.
200
+ A row that selects through its own onClick is unaffected — the click lands on
201
+ the name and bubbles to the row as before. */
202
+ /* A hidden row keeps its eye showing, back in the flow, so it can always be
203
+ un-hidden without a scrim parked over its struck-through name. */
204
+ .es-tree-row.is-hidden .es-tree-actions {
205
+ position: static;
206
+ opacity: 1;
207
+ background: none;
208
+ padding-left: 0;
209
+ }
210
+
211
+ .es-tree-del,
212
+ .es-tree-eye {
213
+ width: 22px;
214
+ height: 22px;
215
+ border: none;
216
+ background: transparent;
217
+ border-radius: 5px;
218
+ cursor: pointer;
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ padding: 0;
223
+ opacity: 0;
224
+ pointer-events: none;
225
+ flex: none;
226
+ }
227
+ .es-tree-del svg,
228
+ .es-tree-eye svg {
229
+ width: 14px;
230
+ height: 14px;
231
+ }
232
+ .es-tree-del {
233
+ color: var(--es-danger);
234
+ transition: opacity 0.12s, background 0.12s, color 0.12s;
235
+ }
236
+ .es-tree-eye {
237
+ color: var(--es-muted);
238
+ transition: opacity 0.12s, background 0.12s;
239
+ }
240
+ .es-tree-row:hover .es-tree-del,
241
+ .es-tree-row:focus-within .es-tree-del,
242
+ .es-tree-row:hover .es-tree-eye,
243
+ .es-tree-row:focus-within .es-tree-eye {
244
+ opacity: 0.7;
245
+ pointer-events: auto;
246
+ }
247
+ .es-tree-del:hover {
248
+ opacity: 1;
249
+ background: var(--es-danger-tint);
250
+ color: var(--es-danger-strong);
251
+ }
252
+ .es-tree-eye:hover {
253
+ opacity: 1;
254
+ background: var(--es-hover);
255
+ }
256
+ .es-tree-row.is-hidden .es-tree-eye {
257
+ opacity: 1;
258
+ pointer-events: auto;
259
+ }
260
+
261
+ /* The ONLY drag source and keyboard reorder route. It may be made invisible;
262
+ it may never be made unreachable — opacity only, never display or visibility. */
263
+ .es-tree-handle {
264
+ position: absolute;
265
+ left: 1px;
266
+ top: 50%;
267
+ transform: translateY(-50%);
268
+ color: var(--es-disabled);
269
+ font-family: inherit;
270
+ font-size: 11px;
271
+ line-height: 1;
272
+ letter-spacing: -2px;
273
+ width: 9px;
274
+ flex: none;
275
+ cursor: grab;
276
+ opacity: 0;
277
+ transition: opacity 0.12s;
278
+ padding: 0;
279
+ border: none;
280
+ background: transparent;
281
+ touch-action: none;
282
+ }
283
+ .es-tree-row:hover .es-tree-handle,
284
+ .es-tree-handle:focus-visible {
285
+ opacity: 1;
286
+ }
287
+ .es-tree-handle:active {
288
+ cursor: grabbing;
289
+ }
290
+ .es-tree-handle:focus-visible {
291
+ outline: 2px solid var(--es-accent);
292
+ outline-offset: 1px;
293
+ border-radius: 3px;
294
+ color: var(--es-text-secondary);
295
+ }
296
+
297
+ /* The caret LEADS the row (One Ladder). A row that does not fold holds the
298
+ column open with the spacer, so every glyph below lines up. */
299
+ .es-tree-caret {
300
+ width: 14px;
301
+ height: 14px;
302
+ margin-left: -2px;
303
+ flex: none;
304
+ padding: 0;
305
+ border: none;
306
+ background: transparent;
307
+ border-radius: 4px;
308
+ color: var(--es-muted);
309
+ display: flex;
310
+ align-items: center;
311
+ justify-content: center;
312
+ cursor: pointer;
313
+ }
314
+ .es-tree-caret:hover {
315
+ background: var(--es-hover);
316
+ color: var(--es-text);
317
+ }
318
+ .es-tree-caret-spacer {
319
+ width: 14px;
320
+ margin-left: -2px;
321
+ flex: none;
322
+ }
323
+ .es-tree-chev {
324
+ transition: transform 0.14s;
325
+ }
326
+ .es-tree-chev.is-open {
327
+ transform: rotate(90deg);
328
+ }
329
+
330
+ /* ── 0.44.0 — the campaign editor's rows ─────────────────────────────────── */
331
+ /* The name as a button, for keyboard selection. It must not move the row by a
332
+ pixel against a row without one: no chrome, no padding, the same flex run. */
333
+ .es-tree-select {
334
+ all: unset;
335
+ box-sizing: border-box;
336
+ flex: 1;
337
+ min-width: 0;
338
+ display: flex;
339
+ align-items: center;
340
+ gap: 6px;
341
+ cursor: pointer;
342
+ border-radius: 4px;
343
+ }
344
+ .es-tree-select:focus-visible {
345
+ outline: 2px solid var(--es-accent);
346
+ outline-offset: 2px;
347
+ }
348
+ /* With a mark or a badge after it, the name hugs its words so the mark sits
349
+ beside them rather than at the far end. A plain row keeps `flex: 1`. */
350
+ .es-tree-select .es-tree-name,
351
+ .es-tree-name:has(~ .es-tree-mark),
352
+ .es-tree-name:has(~ .es-tree-badge) {
353
+ flex: 0 1 auto;
354
+ }
355
+ /* A named state after the name — quiet, never a control. */
356
+ .es-tree-mark {
357
+ flex: none;
358
+ display: inline-flex;
359
+ color: var(--es-muted);
360
+ }
361
+ .es-tree-mark svg,
362
+ .es-tree-trailing svg {
363
+ width: 13px;
364
+ height: 13px;
365
+ }
366
+ .es-tree-badge {
367
+ flex: none;
368
+ font-size: 10px;
369
+ font-weight: 500;
370
+ font-variant-numeric: tabular-nums;
371
+ color: var(--es-muted);
372
+ background: var(--es-chip-bg);
373
+ border-radius: 4px;
374
+ padding: 1px 4px;
375
+ }
376
+ .es-tree-trailing {
377
+ flex: none;
378
+ display: inline-flex;
379
+ color: var(--es-muted);
380
+ }
381
+ /* Any other row action, in the neutral colour — the eye's twin. */
382
+ .es-tree-act {
383
+ width: 22px;
384
+ height: 22px;
385
+ border: none;
386
+ background: transparent;
387
+ border-radius: 5px;
388
+ cursor: pointer;
389
+ color: var(--es-muted);
390
+ display: flex;
391
+ align-items: center;
392
+ justify-content: center;
393
+ padding: 0;
394
+ opacity: 0;
395
+ pointer-events: none;
396
+ flex: none;
397
+ transition: opacity 0.12s, background 0.12s;
398
+ }
399
+ .es-tree-act svg {
400
+ width: 14px;
401
+ height: 14px;
402
+ }
403
+ .es-tree-row:hover .es-tree-act,
404
+ .es-tree-row:focus-within .es-tree-act {
405
+ opacity: 0.7;
406
+ pointer-events: auto;
407
+ }
408
+ .es-tree-act:hover {
409
+ opacity: 1;
410
+ background: var(--es-hover);
411
+ }
412
+ .es-tree-act:disabled {
413
+ color: var(--es-disabled);
414
+ cursor: default;
415
+ }
416
+ /* A toggle that is ON stays visible at rest, like a hidden row's eye: the state
417
+ has to be readable without hovering. */
418
+ .es-tree-act[aria-pressed="true"] {
419
+ opacity: 1;
420
+ pointer-events: auto;
421
+ color: var(--es-accent);
422
+ }
423
+ /* …which means its strip must show too. Same answer as a hidden row's eye: the
424
+ strip goes back into the flow, with no scrim over the name, and the other
425
+ actions in it still wait for hover. */
426
+ .es-tree-actions:has(.es-tree-act[aria-pressed="true"]) {
427
+ position: static;
428
+ opacity: 1;
429
+ background: none;
430
+ padding-left: 0;
431
+ }
432
+ /* In the flow, the actions still waiting for hover must not hold their width
433
+ open at rest — that would cut the name down to make room for buttons nobody
434
+ can see. They come back when the row is hovered or focus enters it (the name
435
+ button is the row's first tab stop, so the keyboard reaches them). */
436
+ .es-tree-actions:has(.es-tree-act[aria-pressed="true"]) > :not([aria-pressed="true"]) {
437
+ display: none;
438
+ }
439
+ .es-tree-row:hover .es-tree-actions:has(.es-tree-act[aria-pressed="true"]) > :not([aria-pressed="true"]),
440
+ .es-tree-row:focus-within .es-tree-actions:has(.es-tree-act[aria-pressed="true"]) > :not([aria-pressed="true"]) {
441
+ display: flex;
442
+ }
443
+
444
+ /* A rename box stands in for the name. It must not move the row by a pixel:
445
+ same font and size, no border, no padding of its own. */
446
+ .es-tree-rename {
447
+ flex: 1 1 auto;
448
+ min-width: 0;
449
+ padding: 0;
450
+ border: 0;
451
+ background: transparent;
452
+ font: inherit;
453
+ font-size: 12px;
454
+ font-weight: 500;
455
+ color: inherit;
456
+ outline: none;
457
+ }
458
+
459
+ /* ── Picking a row up (efficient-easel/drag, 0.74.0) ───────────────────────
460
+ The whole row is the handle. It says so four ways, and none of them is a
461
+ target of its own: the grab cursor, a faint ⋮ at its left on hover, a hint
462
+ after 0.7 s, and — while it moves — the row lifted with a shadow. */
463
+ .es-tree-row.is-draggable,
464
+ .es-tree-row.is-draggable .es-tree-select {
465
+ cursor: grab;
466
+ }
467
+ .es-tree-row.is-draggable:active,
468
+ .es-tree-row.is-draggable:active .es-tree-select {
469
+ cursor: grabbing;
470
+ }
471
+ .es-tree-grip {
472
+ position: absolute;
473
+ left: 2px;
474
+ top: 50%;
475
+ transform: translateY(-50%);
476
+ font-size: 12px;
477
+ line-height: 1;
478
+ color: var(--es-disabled);
479
+ opacity: 0;
480
+ transition: opacity 0.12s;
481
+ pointer-events: none;
482
+ }
483
+ .es-tree-row.is-draggable:hover .es-tree-grip {
484
+ opacity: 0.8;
485
+ }
486
+ /* The hint waits 0.7 s on the way in and none on the way out — a delay in the
487
+ transition, not a timer, so nothing re-renders while the pointer rests. */
488
+ .es-tree-hint {
489
+ position: absolute;
490
+ left: 24px;
491
+ top: calc(100% + 4px);
492
+ z-index: 5;
493
+ padding: 3px 7px;
494
+ border-radius: 6px;
495
+ background: var(--es-toast-bg);
496
+ color: var(--es-on-accent);
497
+ font-size: 11px;
498
+ line-height: 1.3;
499
+ white-space: nowrap;
500
+ pointer-events: none;
501
+ opacity: 0;
502
+ transition: opacity 0.1s;
503
+ }
504
+ .es-tree-row.is-draggable:hover .es-tree-hint {
505
+ opacity: 1;
506
+ transition-delay: 0.7s;
507
+ }
508
+ .es-tree-row.is-dragging .es-tree-hint,
509
+ .es-tree-row.is-lifting .es-tree-hint {
510
+ display: none;
511
+ }
512
+ /* On for the instant the browser takes the drag picture: the row that follows
513
+ the pointer is this one, raised. */
514
+ .es-tree-row.is-lifting {
515
+ background: var(--es-raised);
516
+ box-shadow: 0 8px 20px rgba(16, 24, 40, 0.16), 0 2px 6px rgba(16, 24, 40, 0.1);
517
+ }
518
+
519
+ /* ── The seam — the gap between two rows, as a place (0.74.0) ───────────────
520
+ A drop between rows lands here, and a host may offer a "+" that adds here.
521
+ IT IS THE GAP: every rule below takes back the space a seam would otherwise
522
+ add, so a list reads exactly the same with seams in it or without. A list's
523
+ gaps are uniform — seams in every gap of it, or in none. */
524
+ .es-tree-seam {
525
+ position: relative;
526
+ z-index: 1;
527
+ flex: none;
528
+ height: var(--es-rhythm-between);
529
+ }
530
+ /* A little more than the gap to aim at, over the rows' own edges. */
531
+ .es-tree-seam::before {
532
+ content: "";
533
+ position: absolute;
534
+ left: 0;
535
+ right: 0;
536
+ top: -4px;
537
+ bottom: -4px;
538
+ }
539
+ .es-tree-seam::after {
540
+ content: "";
541
+ position: absolute;
542
+ left: 8px;
543
+ right: 8px;
544
+ top: calc(50% - 1px);
545
+ height: 2px;
546
+ background: var(--es-accent);
547
+ border-radius: 2px;
548
+ opacity: 0;
549
+ pointer-events: none;
550
+ transition: opacity 0.12s;
551
+ }
552
+ .es-tree-seam.is-drop::after,
553
+ .es-tree-seam.has-add:hover::after,
554
+ .es-tree-seam.has-add:focus-within::after {
555
+ opacity: 1;
556
+ }
557
+ .es-tree-seam-add {
558
+ position: absolute;
559
+ left: 50%;
560
+ top: 50%;
561
+ width: 18px;
562
+ height: 18px;
563
+ margin: -9px 0 0 -9px;
564
+ padding: 0;
565
+ border: none;
566
+ border-radius: 50%;
567
+ background: var(--es-accent);
568
+ color: var(--es-on-accent);
569
+ font: 600 13px/18px var(--es-font);
570
+ cursor: pointer;
571
+ opacity: 0;
572
+ transform: scale(0.7);
573
+ transition: opacity 0.12s, transform 0.12s;
574
+ }
575
+ .es-tree-seam.has-add:hover .es-tree-seam-add,
576
+ .es-tree-seam-add:focus-visible {
577
+ opacity: 1;
578
+ transform: scale(1);
579
+ }
580
+ .es-tree-seam-add:focus-visible {
581
+ outline: 2px solid var(--es-accent);
582
+ outline-offset: 2px;
583
+ }
584
+ /* While something is being dropped, the line alone. */
585
+ .es-tree-seam.is-drop .es-tree-seam-add {
586
+ opacity: 0;
587
+ }
588
+ /* The top level stacks as blocks, and margins there collapse: a seam after a
589
+ subtree, beside a group label, or at an end hands its height back. */
590
+ .es-tree > .es-subtree + .es-tree-seam,
591
+ .es-tree > .es-tree-group + .es-tree-seam,
592
+ .es-tree > .es-tree-seam:last-child {
593
+ margin-top: calc(-1 * var(--es-rhythm-between));
594
+ }
595
+ .es-tree > .es-tree-seam:has(+ .es-tree-group),
596
+ .es-tree > .es-tree-seam:first-child {
597
+ margin-bottom: calc(-1 * var(--es-rhythm-between));
598
+ }
599
+ /* …once: a seam both after a subtree and before a label has already handed its
600
+ height back above. */
601
+ .es-tree > .es-subtree + .es-tree-seam:has(+ .es-tree-group) {
602
+ margin-bottom: 0;
603
+ }
604
+ /* One level down is a flex column with a gap: a seamed list's seams ARE its
605
+ gaps, a nested list keeps the gap-and-margin it had, and the ends reach into
606
+ the list's own margins. */
607
+ .es-subtree:has(> .es-tree-seam) {
608
+ gap: 0;
609
+ }
610
+ .es-subtree:has(> .es-tree-seam) > .es-subtree {
611
+ margin-top: calc(2 * var(--es-rhythm-between));
612
+ }
613
+ .es-subtree > .es-tree-seam:first-child {
614
+ margin-top: calc(-1 * var(--es-rhythm-between));
615
+ }
616
+ .es-subtree > .es-tree-seam:last-child {
617
+ margin-bottom: calc(-1 * var(--es-rhythm-between));
618
+ }