efficient-easel 0.0.0 → 0.97.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 +818 -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 +306 -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-6OPSJGAI.js +1 -0
  17. package/dist/chunk-6PS6STGA.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-D2BCHTQR.js +1 -0
  23. package/dist/chunk-FI23ALVJ.js +1 -0
  24. package/dist/chunk-GGTPNGFJ.js +1 -0
  25. package/dist/chunk-H4B72XCJ.js +1 -0
  26. package/dist/chunk-H6DDIBQ4.js +1 -0
  27. package/dist/chunk-KDIVA6WD.js +1 -0
  28. package/dist/chunk-KRXQM65X.js +1 -0
  29. package/dist/chunk-MCLBHENY.js +1 -0
  30. package/dist/chunk-MFIRYGHW.js +1 -0
  31. package/dist/chunk-NKB3KVAB.js +1 -0
  32. package/dist/chunk-O2NLQBFX.js +1 -0
  33. package/dist/chunk-OGZFDJ6G.js +1 -0
  34. package/dist/chunk-OWBO4WKA.js +0 -0
  35. package/dist/chunk-QB3BZIRA.js +5 -0
  36. package/dist/chunk-RE4LP46F.js +1 -0
  37. package/dist/chunk-STFMZJKU.js +14 -0
  38. package/dist/chunk-SUO5627P.js +1 -0
  39. package/dist/chunk-TDA4HH54.js +1 -0
  40. package/dist/chunk-WK3AFPXY.js +1 -0
  41. package/dist/chunk-YIMRTIBN.js +3 -0
  42. package/dist/chunk-YNZZYHA4.js +450 -0
  43. package/dist/chunk-YRLPFZW3.js +1 -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 +375 -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-DChbUXdS.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-D68pb3Bx.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-41704JE4.d.ts +112 -0
  102. package/dist/types-CEqUFH2j.d.ts +129 -0
  103. package/dist/types-CuNiSnX6.d.ts +204 -0
  104. package/dist/types-DNrn_w4V.d.ts +88 -0
  105. package/dist/types-Dy6oR6Z2.d.ts +153 -0
  106. package/dist/types-pGBXevBU.d.ts +131 -0
  107. package/dist/useNodeOps-BF0fQsif.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,273 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { S as SettingsBucket } from '../buckets-1vRlQceY.js';
4
+ import { I as InspectorFields, a as InspectorFieldDef, F as FieldRenderer, b as FieldTextProps, c as FieldNumberProps, d as FieldSelectProps, e as FieldOption } from '../types-CEqUFH2j.js';
5
+ export { A as ArrayFieldDef, C as CustomFieldDef, f as CustomFieldRenderProps, N as NumberFieldDef, O as ObjectFieldDef, R as RadioFieldDef, S as SelectFieldDef, g as SlotFieldDef, T as TextFieldDef, h as TextareaFieldDef } from '../types-CEqUFH2j.js';
6
+ import { R as ResolvableItem } from '../resolve-D68pb3Bx.js';
7
+ export { F as FieldsWords } from '../words-CD3WADyb.js';
8
+ import '../types-41704JE4.js';
9
+
10
+ interface InspectorProps {
11
+ /** The selected node's fields, already resolved by the host. */
12
+ fields: InspectorFields;
13
+ /** The node's props. */
14
+ values: Record<string, unknown>;
15
+ /** One field written. The host turns it into one change of the document. */
16
+ onChange: (name: string, value: unknown) => void;
17
+ readOnly?: boolean;
18
+ /** Extra props for every custom render (the node, the root, the device…). */
19
+ context?: Record<string, unknown>;
20
+ /** The open tab. Absent: the Inspector keeps its own. */
21
+ tab?: SettingsBucket;
22
+ onTab?: (tab: SettingsBucket) => void;
23
+ /** False: no tab strip, every row in one list (the email designer's phone view). */
24
+ tabs?: boolean;
25
+ /** A glyph per tab (both editors draw one). */
26
+ tabIcons?: Partial<Record<SettingsBucket, ReactNode>>;
27
+ /** A field type's drawing, swapped in by the host. */
28
+ renderers?: Partial<Record<InspectorFieldDef['type'], FieldRenderer>>;
29
+ /** Unique per panel, e.g. the selected node's id: every field id starts with it. */
30
+ idPrefix?: string;
31
+ /** The tab strip's name. Default "Settings" (`EditorStrings.panel.settings`). */
32
+ ariaLabel?: string;
33
+ /** Drawn after the rows, on every tab (an app's own trailing rows). */
34
+ children?: ReactNode;
35
+ }
36
+ /**
37
+ * THE SETTINGS PANEL'S BODY (blueprint step 4.1) — what `<Puck.Fields>` was in
38
+ * both editors, without the hashed class names both had to hunt.
39
+ *
40
+ * - THE TABS ARE THE FIELDS' OWN. Which tab a field opens is said on the field
41
+ * (`bucket`, ./plan); the strip offers only the tabs that hold a row, in the
42
+ * owner's order, and none at all when there is only one.
43
+ * - A ROW ON ANOTHER TAB IS HIDDEN, NOT UNMOUNTED. The email designer's Logo
44
+ * re-bakes its colours from a field on a tab you are not looking at; taking
45
+ * the row out of the tree would stop that.
46
+ * - EVERY ROW IS ONE `.es-insp-row` WITH ITS NAME ON IT (`data-field`), a
47
+ * public class an app may style, where Puck's wrapper had a hashed one. A row
48
+ * that draws nothing takes no space (`:empty`).
49
+ * - THE SPACING IS THE PANEL'S (panel.css `--es-rhythm-*`): a row adds none of
50
+ * its own, so the rhythm is defined once for both editors.
51
+ * - A TAB'S KEY IS NOT ITS WORD. `bucket: 'Style'` names the tab; what the tab
52
+ * says is `EditorStrings.panel.buckets.Style`, from the strings provider.
53
+ */
54
+ declare function Inspector({ fields, values, onChange, readOnly, context, tab, onTab, tabs, tabIcons, renderers, idPrefix, ariaLabel, children, }: InspectorProps): react.JSX.Element;
55
+
56
+ interface InspectorFieldProps {
57
+ field: InspectorFieldDef;
58
+ name: string;
59
+ /** Unique on the page: the label's `for`, and what a custom render gets as `id`. */
60
+ id: string;
61
+ value: unknown;
62
+ onChange: (value: unknown) => void;
63
+ readOnly?: boolean;
64
+ /** Extra props for a custom render (the node, the root, the device…). */
65
+ context?: Record<string, unknown>;
66
+ /** A field type's drawing, swapped in by the host. */
67
+ renderers?: Partial<Record<InspectorFieldDef['type'], FieldRenderer>>;
68
+ /**
69
+ * An array's open item, when the Inspector holds it (the item takes the panel
70
+ * over). Absent: the list keeps its own.
71
+ */
72
+ openItem?: number | null;
73
+ onOpenItem?: (index: number | null) => void;
74
+ }
75
+ /** The label a row shows: the field's own, else its key, as Puck did. */
76
+ declare const labelOf: (field: InspectorFieldDef, name: string) => string;
77
+ /**
78
+ * ONE FIELD, drawn by its type — what Puck's `AutoField` was (blueprint 4.1).
79
+ *
80
+ * custom the field's own render, called with Puck's props plus `context`;
81
+ * text, textarea, number, select
82
+ * a SettingsField row (quiet label, control under it) holding the
83
+ * shell's own control, never the browser's;
84
+ * radio the panel's segmented strip;
85
+ * object a group of its members, each written back into the object;
86
+ * slot nothing: a slot's children are on the canvas and in Layers;
87
+ * array the list field (4.2): a row per item, open one to edit its own
88
+ * fields, each written back into the array.
89
+ *
90
+ * A marker, or a field with `visible: false`, draws nothing.
91
+ */
92
+ declare function InspectorField(props: InspectorFieldProps): ReactNode;
93
+
94
+ /** One drawn row: a field, its name, and the tab it opens. */
95
+ interface PlannedRow {
96
+ name: string;
97
+ field: InspectorFieldDef;
98
+ bucket: SettingsBucket;
99
+ }
100
+ interface FieldPlan {
101
+ /** Every field that draws, in the order declared. */
102
+ rows: PlannedRow[];
103
+ /** The tabs to offer: the buckets that hold a row, in the owner's order. */
104
+ buckets: SettingsBucket[];
105
+ }
106
+ /**
107
+ * WHICH TAB EACH FIELD OPENS — read off the fields themselves (blueprint 4.1:
108
+ * "the tab is declared by the field, never found by hashed class names").
109
+ *
110
+ * In declaration order, a field's own `bucket` starts that bucket, and every
111
+ * field after it without one stays in it; the panel starts in Content. That one
112
+ * rule covers both editors as they are written today: the email designer puts a
113
+ * mark in front of each tab's run (its `FIELD_BUCKETS` map), the website puts a
114
+ * heading there (its `group(...)` headers), and a mark is just a field with
115
+ * `marker: true` that draws nothing.
116
+ *
117
+ * A slot counts for no tab — it draws nothing in the panel — and a field with
118
+ * `visible: false` neither draws nor counts, so a tab whose fields are all
119
+ * hidden is not offered (the owner's rule: an empty tab is hidden, never drawn).
120
+ */
121
+ declare function planFields(fields: InspectorFields): FieldPlan;
122
+ /**
123
+ * Every field's bucket, by name — what "copy style" asks (a look is what is not
124
+ * Content). The walk both editors did on their own (the website's
125
+ * `style-clipboard.ts`, the email designer's `FIELD_BUCKETS`) is this one.
126
+ */
127
+ declare function bucketsOf(fields: InspectorFields): Map<string, SettingsBucket>;
128
+
129
+ /** What a block may declare about its fields. */
130
+ interface FieldsBlock {
131
+ fields?: object;
132
+ resolveFields?: (data: ResolvableItem, params: {
133
+ changed: Record<string, boolean>;
134
+ fields: InspectorFields;
135
+ lastFields: InspectorFields;
136
+ lastData: ResolvableItem | null;
137
+ appState: unknown;
138
+ parent: ResolvableItem | null;
139
+ }) => InspectorFields | Promise<InspectorFields>;
140
+ }
141
+ interface ResolvedFieldsInput {
142
+ /** The selected node's id, or "root". A new id starts again from its declared fields. */
143
+ id: string;
144
+ /** The node as it is now. A new object is a change, and is resolved again. */
145
+ node: ResolvableItem | null;
146
+ block: FieldsBlock | undefined;
147
+ /** The node that holds it, or null at the top. */
148
+ parent?: ResolvableItem | null;
149
+ /** What a resolver is told about the editor (Puck's public `{ data, ui }`). */
150
+ appState?: unknown;
151
+ }
152
+ /**
153
+ * A NODE'S FIELDS, RESOLVED AS PUCK RESOLVES THEM — `useRegisterFieldsSlice`
154
+ * in Puck 0.20, which both editors' `resolveFields` were written against.
155
+ *
156
+ * - A new selection starts from the block's DECLARED fields (`lastFields` is
157
+ * those) and is then resolved.
158
+ * - A change to the node resolves again, with the last answer as
159
+ * `lastFields`, the last node resolved for this id as `lastData`, and
160
+ * `changed` saying which props differ from it.
161
+ * - Every resolver is awaited, sync or not; an answer that arrives after the
162
+ * selection has moved on is dropped.
163
+ * - `loading` turns on only when an answer takes longer than 50 ms.
164
+ *
165
+ * `appState` and `parent` are read when a resolution starts, never watched:
166
+ * Puck re-resolved on the node's change alone.
167
+ */
168
+ declare function useResolvedFields({ id, node, block, parent, appState }: ResolvedFieldsInput): {
169
+ fields: InspectorFields;
170
+ loading: boolean;
171
+ };
172
+
173
+ /**
174
+ * A LIST VALUE (blueprint step 4.2) — slides, buttons, links, any `array`
175
+ * field — drawn the way Layers draws a page: one row per item, the same rows,
176
+ * the same drag (the whole row picks up), the same ⌥↑ / ⌥↓, and a "+" between
177
+ * two rows that adds one there. Pressing a row opens that item in the panel,
178
+ * with a way back at the top.
179
+ *
180
+ * It replaces Puck's ArrayField, which drew its own look and ran its own drag.
181
+ * THE DATA DOES NOT CHANGE: a list is the array Puck stored, and every write is
182
+ * that array with an item added, moved, copied or taken out. No id is added to
183
+ * an item (Puck's `_arrayId` lived in its UI state, never in the data, and so
184
+ * does a row's key here — its position).
185
+ *
186
+ * CONTROLLED, both the list and which item is open: the Inspector holds them,
187
+ * so an editor can keep the open item across a re-render of the panel.
188
+ *
189
+ * Its words ("Add slide", "2 of 5", the rows' drag hint) are the strings
190
+ * provider's — `EditorStrings.fields.list` and `.tree` — English with none.
191
+ */
192
+ interface ListFieldProps<T> {
193
+ /** The list's name, e.g. "Slides". Also the back button's word. */
194
+ label: string;
195
+ /** A glyph before the name, as a settings row wears one. */
196
+ icon?: ReactNode;
197
+ /** One item's word, lower case, e.g. "slide": "Add slide", "Delete slide". Default "item" (`EditorStrings.fields.list.item`). */
198
+ noun?: string;
199
+ items: readonly T[];
200
+ onChange: (next: T[]) => void;
201
+ /** A fresh item for "+" (Puck's `defaultItemProps`, copied). */
202
+ newItem: () => T;
203
+ /** The words after an item's name in its row (Puck's `getItemSummary`). */
204
+ summary?: (item: T, index: number) => ReactNode;
205
+ /** An item's own name; default "<Noun> <n>". */
206
+ itemName?: (item: T, index: number) => string;
207
+ /** A small picture at the start of the row. */
208
+ thumb?: (item: T, index: number) => ReactNode;
209
+ min?: number;
210
+ max?: number;
211
+ readOnly?: boolean;
212
+ /** The item open in the panel, by position, or null for the list. */
213
+ open: number | null;
214
+ onOpen: (index: number | null) => void;
215
+ /** The open item's own settings. `change` writes the item. */
216
+ renderItem: (item: T, index: number, change: (next: T) => void) => ReactNode;
217
+ }
218
+ /** The list with `item` put at `index`. */
219
+ declare function insertAt<T>(items: readonly T[], index: number, item: T): T[];
220
+ /** The list with the item at `from` moved to `to` (a final position). */
221
+ declare function moveTo<T>(items: readonly T[], from: number, to: number): T[];
222
+ /** The list without the item at `index`. */
223
+ declare function removeAt<T>(items: readonly T[], index: number): T[];
224
+ /**
225
+ * A copy of an item: a deep copy of plain data, so editing the copy never
226
+ * writes through to the original (a list item is JSON — Puck stored it so).
227
+ */
228
+ declare function copyItem<T>(item: T): T;
229
+ declare function ListField<T>({ label, icon, noun: ownNoun, items, onChange, newItem, summary, itemName, thumb, min, max, readOnly, open, onOpen, renderItem, }: ListFieldProps<T>): react.JSX.Element;
230
+
231
+ /**
232
+ * A text value: the shell's own box, one line or several (blueprint step 4.1,
233
+ * "native inputs become shell controls"). The look is `fields.css`
234
+ * (`.es-fin`), so a row in the email designer and a row in the website editor
235
+ * are the same box instead of Puck's input under two sets of overrides.
236
+ *
237
+ * CONTROLLED: every keystroke goes to `onChange`; the host decides what a
238
+ * keystroke costs (the editors write a draft and save on their own clock).
239
+ */
240
+ declare function FieldText({ id, value, onChange, placeholder, multiline, rows, readOnly, ariaLabel, ariaLabelledBy, }: FieldTextProps): react.JSX.Element;
241
+
242
+ /** A number held to its bounds and, when a step is given, to the step's grid. */
243
+ declare function clampNumber(n: number, min?: number, max?: number): number;
244
+ /** What a typed string means as a number: null for nothing or not-a-number. */
245
+ declare function parseTyped(text: string): number | null;
246
+ /**
247
+ * A number value (blueprint step 4.1): the shell box, an optional unit after it
248
+ * and − / + beside it.
249
+ *
250
+ * It keeps the TYPED TEXT while you type, so "1." and "-" are allowed on the way
251
+ * to a number, and writes only when the text is a number. It writes as you type
252
+ * (the canvas follows), clamps to `min` / `max` when you leave the box, and
253
+ * arrow keys and the two buttons move by `step` (default 1). An empty box writes
254
+ * `undefined`: the setting goes back to the block's own.
255
+ */
256
+ declare function FieldNumber({ id, value, onChange, min, max, step, unit, steppers, placeholder, readOnly, ariaLabel, ariaLabelledBy, }: FieldNumberProps): react.JSX.Element;
257
+
258
+ /** The option a stored value names: equal by `Object.is`, so `0`, `false` and `''` are values too. */
259
+ declare function optionFor<V>(options: readonly FieldOption<V>[], value: V | undefined): FieldOption<V> | undefined;
260
+ /**
261
+ * One of a fixed set (blueprint step 4.1): a button that shows the choice and
262
+ * opens a list under it. The shell's control, not the browser's `<select>`, so
263
+ * it is the same box and the same list in both editors and on every system.
264
+ *
265
+ * Keyboard as a listbox: ↓ / ↑ / Enter / Space open it on the button; in the
266
+ * list ↓ ↑ Home End move, Enter or Space choose, Escape closes and gives the
267
+ * button its focus back, a letter jumps to the next option that starts with it.
268
+ * A click outside closes it (a CLICK, not a pointerdown: closing early moves the
269
+ * row and loses the press — the colour row learned that in 4.6).
270
+ */
271
+ declare function FieldSelect<V>({ id, value, options, onChange, placeholder, readOnly, ariaLabel, ariaLabelledBy, }: FieldSelectProps<V>): react.JSX.Element;
272
+
273
+ export { FieldNumber, FieldNumberProps, FieldOption, type FieldPlan, FieldRenderer, FieldSelect, FieldSelectProps, FieldText, FieldTextProps, type FieldsBlock, Inspector, InspectorField, InspectorFieldDef, type InspectorFieldProps, InspectorFields, type InspectorProps, ListField, type ListFieldProps, type PlannedRow, type ResolvedFieldsInput, SettingsBucket, bucketsOf, clampNumber, copyItem, insertAt, labelOf, moveTo, optionFor, parseTyped, planFields, removeAt, useResolvedFields };
@@ -0,0 +1,46 @@
1
+ import{LayersTree as e,TreeSeam as n,TreeRow as t,TreeRowAction as l}from"../chunk-STFMZJKU.js";import{SettingsField as a,SETTINGS_BUCKETS as r,SettingsTabs as i,SettingsGroup as s}from"../chunk-67VZRQLG.js";import{DEFAULT_FIELDS_WORDS as o}from"../chunk-SUO5627P.js";import{DEFAULT_TREE_WORDS as c}from"../chunk-H4B72XCJ.js";import{DEFAULT_PANEL_WORDS as d}from"../chunk-7A7O2JWQ.js";export{useResolvedFields}from"../chunk-YRLPFZW3.js";import{SettingsSegmented as u}from"../chunk-QB3BZIRA.js";import{useWords as m}from"../chunk-MFIRYGHW.js";import"../chunk-BZ5ASXOL.js";import{useTreeDrag as h,gapToMoveIndex as p}from"../chunk-6PS6STGA.js";import"../chunk-YNZZYHA4.js";import"../chunk-KRXQM65X.js";import"../chunk-NKB3KVAB.js";import{useState as b,useEffect as f,useId as v,useRef as y}from"react";import{jsxs as g,jsx as k,Fragment as x}from"react/jsx-runtime";function N(e,n,t){let l=e;return"number"==typeof n&&l<n&&(l=n),"number"==typeof t&&l>t&&(l=t),l}function w(e){const n=e.trim().replace(",",".");if(""===n||"-"===n||"."===n||"-."===n)return null;const t=Number(n);return Number.isFinite(t)?t:null}function C(e){const n=String(e),t=n.indexOf(".");return t<0?0:n.length-t-1}function O({id:e,value:n,onChange:t,min:l,max:a,step:r=1,unit:i,steppers:s=!1,placeholder:c,readOnly:d=!1,ariaLabel:u,ariaLabelledBy:h}){const p=m("fields",o),v="number"==typeof n&&Number.isFinite(n)?String(n):"",[y,x]=b(v);f(()=>{x(e=>w(e)===("number"==typeof n?n:null)?e:v)},[n,v]);const O=e=>{d||t(e)},$=e=>{const t=N(function(e,n){const t=Math.max(C(e),C(n));return Number((e+n).toFixed(t))}("number"==typeof n&&Number.isFinite(n)?n:l??0,e*r),l,a);x(String(t)),O(t)};/* @__PURE__ */
2
+ return g("div",{className:"es-fnum",children:[
3
+ /* @__PURE__ */k("input",{id:e,type:"text",inputMode:"decimal",className:"es-fin es-fnum-box",value:y,placeholder:c,readOnly:d,"aria-label":u,"aria-labelledby":h,onChange:e=>{const n=e.target.value;if(x(n),""===n.trim())O(void 0);else{const e=w(n);null!==e&&O(e)}},onBlur:()=>{const e=w(y);if(null===e)return void x(v);const t=N(e,l,a);x(String(t)),t!==n&&O(t)},onKeyDown:e=>{"ArrowUp"!==e.key&&"ArrowDown"!==e.key||(e.preventDefault(),$("ArrowUp"===e.key?1:-1))}}),i?/* @__PURE__ */k("span",{className:"es-fnum-unit",children:i}):null,s?/* @__PURE__ */g("span",{className:"es-fnum-steps",children:[
4
+ /* @__PURE__ */k("button",{type:"button",className:"es-fbtn","aria-label":p.less,disabled:d,onClick:()=>$(-1),children:"−"}),
5
+ /* @__PURE__ */k("button",{type:"button",className:"es-fbtn","aria-label":p.more,disabled:d,onClick:()=>$(1),children:"+"})]}):null]})}function $(e,n){return e.find(e=>Object.is(e.value,n))}function j({id:e,value:n,options:t,onChange:l,placeholder:a,readOnly:r=!1,ariaLabel:i,ariaLabelledBy:s}){const c=m("fields",o),d=v(),[u,h]=b(!1),[p,x]=b(0),N=y(null),w=y(null),C=y(null),O=$(t,n),j=O?t.indexOf(O):-1,I=()=>{r||0===t.length||(x(j>=0?j:0),h(!0))},S=e=>{h(!1),e&&w.current?.focus()},L=e=>{const a=t[e];a&&!a.disabled&&(Object.is(a.value,n)||l(a.value),S(!0))};f(()=>{if(!u)return;C.current?.focus();const e=N.current?.ownerDocument;if(!e)return;const n=e=>{N.current&&!N.current.contains(e.target)&&h(!1)};return e.addEventListener("click",n,!0),()=>e.removeEventListener("click",n,!0)},[u]);const D=e=>{const n=t.length;if(0===n)return;let l=(e%n+n)%n;for(let a=0;a<n&&t[l]?.disabled;a++)l=(l+(e>=p?1:-1)+n)%n;x(l)};/* @__PURE__ */
6
+ return g("div",{className:"es-fsel",ref:N,children:[
7
+ /* @__PURE__ */g("button",{ref:w,id:e,type:"button",className:"es-fin es-fsel-button","aria-haspopup":"listbox","aria-expanded":u,"aria-controls":u?d:void 0,"aria-label":i,"aria-labelledby":s,disabled:r,onClick:()=>u?S(!1):I(),onKeyDown:e=>{"ArrowDown"!==e.key&&"ArrowUp"!==e.key||(e.preventDefault(),I())},children:[
8
+ /* @__PURE__ */k("span",{className:O?"es-fsel-value":"es-fsel-value is-empty",children:O?O.label:a??c.choose}),
9
+ /* @__PURE__ */k("span",{className:"es-fsel-chev","aria-hidden":"true"})]}),u?/* @__PURE__ */k("ul",{ref:C,id:d,role:"listbox",tabIndex:-1,className:"es-fsel-list","aria-activedescendant":`${d}-${p}`,"aria-labelledby":s,"aria-label":s?void 0:i,onKeyDown:e=>{switch(e.key){case"ArrowDown":return e.preventDefault(),void D(p+1);case"ArrowUp":return e.preventDefault(),void D(p-1);case"Home":return e.preventDefault(),void D(0);case"End":return e.preventDefault(),void D(t.length-1);case"Enter":case" ":return e.preventDefault(),void L(p);case"Escape":return e.preventDefault(),e.stopPropagation(),void S(!0);case"Tab":return void S(!1);default:if(1===e.key.length&&/\S/.test(e.key)){const n=e.key.toLowerCase(),l=t.length;for(let e=1;e<=l;e++){const a=(p+e)%l;if(!t[a]?.disabled&&t[a]?.label.toLowerCase().startsWith(n))return void x(a)}}}},children:t.map((e,n)=>/* @__PURE__ */k("li",{id:`${d}-${n}`,role:"option","aria-selected":n===j,"aria-disabled":e.disabled||void 0,className:`es-fsel-option${n===p?" is-active":""}${n===j?" is-chosen":""}`,onMouseEnter:()=>x(n),onClick:()=>L(n),children:e.label},`${n}-${String(e.value)}`))}):null]})}function I({id:e,value:n,onChange:t,placeholder:l,multiline:a=!1,rows:r=3,readOnly:i=!1,ariaLabel:s,ariaLabelledBy:o}){const c=n??"";return a?/* @__PURE__ */k("textarea",{id:e,className:"es-fin es-fin--area",value:c,rows:r,placeholder:l,readOnly:i,"aria-label":s,"aria-labelledby":o,onChange:e=>t(e.target.value)}):/* @__PURE__ */k("input",{id:e,type:"text",className:"es-fin",value:c,placeholder:l,readOnly:i,"aria-label":s,"aria-labelledby":o,onChange:e=>t(e.target.value)})}function S(e,n,t){const l=e.slice();return l.splice(Math.max(0,Math.min(n,l.length)),0,t),l}function L(e,n,t){const l=e.slice(),[a]=l.splice(n,1);return l.splice(Math.max(0,Math.min(t,l.length)),0,a),l}function D(e,n){return e.filter((e,t)=>t!==n)}function M(e){return null===e||"object"!=typeof e?e:JSON.parse(JSON.stringify(e))}function A(){/* @__PURE__ */
10
+ return g("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",children:[
11
+ /* @__PURE__ */k("circle",{cx:"9",cy:"6",r:"1.6"}),
12
+ /* @__PURE__ */k("circle",{cx:"15",cy:"6",r:"1.6"}),
13
+ /* @__PURE__ */k("circle",{cx:"9",cy:"12",r:"1.6"}),
14
+ /* @__PURE__ */k("circle",{cx:"15",cy:"12",r:"1.6"}),
15
+ /* @__PURE__ */k("circle",{cx:"9",cy:"18",r:"1.6"}),
16
+ /* @__PURE__ */k("circle",{cx:"15",cy:"18",r:"1.6"})]})}var B={parentId:"list",slot:"items"};function F({label:a,icon:r,noun:i,items:s,onChange:d,newItem:u,summary:b,itemName:f,thumb:v,min:y=0,max:N,readOnly:w=!1,open:C,onOpen:O,renderItem:$}){const j=m("fields",o).list,I=m("tree",c),F=i??j.item,P=!w&&("number"!=typeof N||s.length<N),K=!w&&s.length>y,Z=(e,n)=>f?f(e,n):j.itemName({noun:F,n:n+1}),E=h({keyOf:e=>String(e),canDrop:(e,n)=>n.parentId===B.parentId&&n.slot===B.slot,onDrop:(e,n)=>{const t=p({...B,index:e},n);t!==e&&d(L(s,e,t))},onStep:(e,n)=>d(L(s,e,e+n)),canStep:(e,n)=>e+n>=0&&e+n<s.length,readOnly:w}),R=e=>{P&&d(S(s,e,u()))},U=e=>{P&&d(S(s,e+1,M(s[e])))},W=e=>{K&&(d(D(s,e)),null!==C&&O(null))};if(null!==C&&C>=0&&C<s.length){const e=s[C];/* @__PURE__ */
17
+ return g("div",{className:"es-list is-open",children:[
18
+ /* @__PURE__ */g("button",{type:"button",className:"es-list-back",onClick:()=>O(null),children:[
19
+ /* @__PURE__ */k("span",{className:"es-list-back-chev","aria-hidden":"true"}),a]}),
20
+ /* @__PURE__ */g("div",{className:"es-list-title",children:[
21
+ /* @__PURE__ */k("span",{className:"es-list-name",children:Z(e,C)}),
22
+ /* @__PURE__ */k("span",{className:"es-list-count",children:j.position({n:C+1,total:s.length})})]}),
23
+ /* @__PURE__ */k("div",{className:"es-list-item",children:$(e,C,e=>{const n=s.slice();n[C]=e,d(n)})}),w?null:/* @__PURE__ */g("div",{className:"es-list-item-actions",children:[
24
+ /* @__PURE__ */k("button",{type:"button",className:"es-fbtn",disabled:!P,onClick:()=>U(C),children:j.duplicate}),
25
+ /* @__PURE__ */k("button",{type:"button",className:"es-fbtn es-fbtn--danger",disabled:!K,onClick:()=>W(C),children:j.deleteItem({noun:F})})]})]})}/* @__PURE__ */
26
+ return g("div",{className:"es-list",children:[
27
+ /* @__PURE__ */g("div",{className:"es-list-head",children:[
28
+ /* @__PURE__ */g("span",{className:"es-list-label",children:[r?/* @__PURE__ */k("span",{className:"es-field-icon","aria-hidden":"true",children:r}):null,a]}),
29
+ /* @__PURE__ */k("span",{className:"es-list-count",children:s.length})]}),s.length?/* @__PURE__ */g(e,{className:"es-list-tree",rootProps:E.rootProps(),children:[s.map((e,a)=>{const r={...B,index:a},i=E.row({item:a,place:r});/* @__PURE__ */
30
+ return g("div",{className:"es-list-entry",children:[a>0?/* @__PURE__ */k(n,{...E.seam({place:r}),onAdd:P?()=>R(a):void 0,label:j.addHere({noun:F})}):null,
31
+ /* @__PURE__ */k(t,{icon:v?v(e,a):/* @__PURE__ */k(A,{}),name:Z(e,a),preview:b?b(e,a):void 0,draggable:i.draggable,dragging:i.dragging,drop:i.drop,rowProps:i.rowProps,words:I,onSelect:()=>O(a),actions:w?void 0:/* @__PURE__ */g(x,{children:[
32
+ /* @__PURE__ */k(l,{kind:"plain",label:j.duplicateItem({noun:F}),disabled:!P,onClick:()=>U(a),children:/* @__PURE__ */g("svg",{width:"13",height:"13",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[
33
+ /* @__PURE__ */k("rect",{x:"9",y:"9",width:"11",height:"11",rx:"2"}),
34
+ /* @__PURE__ */k("path",{d:"M5 15V6a2 2 0 0 1 2-2h9"})]})}),
35
+ /* @__PURE__ */k(l,{kind:"delete",label:j.deleteItem({noun:F}),disabled:!K,onClick:()=>W(a),children:/* @__PURE__ */k("svg",{width:"13",height:"13",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round","aria-hidden":"true",children:/* @__PURE__ */k("path",{d:"M4 7h16M10 11v6M14 11v6M6 7l1 12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-12M9 7V4h6v3"})})})]})})]},a)}),
36
+ /* @__PURE__ */k(n,{...E.seam({place:{...B,index:s.length}})})]}):null,w?null:/* @__PURE__ */k("button",{type:"button",className:"es-list-add",disabled:!P,onClick:()=>R(s.length),children:j.add({noun:F})})]})}var P=(e,n)=>"string"==typeof e.label&&e.label?e.label:n;function K({field:e,name:n,id:t,value:l,onChange:a,readOnly:r,context:i,renderers:o}){const[c,d]=b(!0),u=l&&"object"==typeof l?l:{};/* @__PURE__ */
37
+ return k(s,{id:`${t}-group`,title:P(e,n),open:c,onToggle:d,children:Object.entries(e.objectFields).map(([e,l])=>/* @__PURE__ */k(E,{field:l,name:`${n}.${e}`,id:`${t}-${e}`,value:u[e],onChange:n=>a({...u,[e]:n}),readOnly:r,context:i,renderers:o},e))})}function Z({field:e,name:n,id:t,value:l,onChange:a,readOnly:r,context:i,renderers:s,openItem:o,onOpenItem:c}){const[d,u]=b(null),m=c?o??null:d,h=c??u,p=Array.isArray(l)?l:[],f=e.getItemSummary;/* @__PURE__ */
38
+ return k(F,{label:P(e,n),icon:e.icon,noun:"string"==typeof e.noun?e.noun:void 0,items:p,onChange:a,newItem:()=>M({...e.defaultItemProps??{}}),summary:f?(e,n)=>f(e,n):void 0,min:e.min,max:e.max,readOnly:r,open:m,onOpen:h,renderItem:(l,a,o)=>Object.entries(e.arrayFields).map(([e,c])=>/* @__PURE__ */k(E,{field:c,name:`${n}[${a}].${e}`,id:`${t}-${a}-${e}`,value:l?.[e],onChange:n=>o({...l,[e]:n}),readOnly:r,context:i,renderers:s},e))})}function E(e){const{field:n,name:t,id:l,value:r,onChange:i,readOnly:s,context:o,renderers:c}=e;if(!n||n.marker||!1===n.visible)return null;const d=c?.[n.type];if(d)return d({field:n,name:t,id:l,value:r,onChange:i,readOnly:s});switch(n.type){case"custom":{const e={...o,field:n,name:t,id:l,value:r,onChange:i,readOnly:s};return n.render(e)}case"text":case"textarea":/* @__PURE__ */
39
+ return k(a,{label:P(n,t),htmlFor:l,icon:n.icon,hint:n.hint,children:/* @__PURE__ */k(I,{id:l,value:"string"==typeof r?r:null==r?"":String(r),onChange:i,placeholder:n.placeholder,multiline:"textarea"===n.type,readOnly:s})});case"number":/* @__PURE__ */
40
+ return k(a,{label:P(n,t),htmlFor:l,icon:n.icon,hint:n.hint,children:/* @__PURE__ */k(O,{id:l,value:"number"==typeof r?r:void 0,onChange:i,min:n.min,max:n.max,step:n.step,placeholder:n.placeholder,readOnly:s})});case"select":/* @__PURE__ */
41
+ return k(a,{label:P(n,t),htmlFor:l,icon:n.icon,hint:n.hint,children:/* @__PURE__ */k(j,{id:l,value:r,options:n.options,onChange:i,readOnly:s})});case"radio":/* @__PURE__ */
42
+ return k(a,{label:P(n,t),icon:n.icon,hint:n.hint,children:/* @__PURE__ */k(u,{ariaLabel:P(n,t),value:r,options:n.options.map(e=>({id:e.value,label:e.label,icon:e.icon,disabled:e.disabled||s})),onChange:e=>{s||i(e)}})});case"object":/* @__PURE__ */
43
+ return k(K,{...e,field:n});case"array":/* @__PURE__ */
44
+ return k(Z,{...e,field:n});case"slot":return null}}var R=e=>r.includes(e);function U(e){let n=r[0];const t=[],l=/* @__PURE__ */new Set;for(const[a,r]of Object.entries(e))r&&(R(r.bucket)&&(n=r.bucket),r.marker||!1===r.visible||(t.push({name:a,field:r,bucket:n}),"slot"!==r.type&&l.add(n)));return{rows:t,buckets:r.filter(e=>l.has(e))}}function W(e){return new Map(U(e).rows.map(e=>[e.name,e.bucket]))}function H({fields:e,values:n,onChange:t,readOnly:l=!1,context:a,tab:r,onTab:s,tabs:o=!0,tabIcons:c,renderers:u,idPrefix:h,ariaLabel:p,children:f}){const y=m("panel",d),x=v(),N=h??`es-insp${x.replace(/:/g,"")}`,w=U(e),[C,O]=b(void 0),$=r??C,j=$&&w.buckets.includes($)?$:w.buckets[0],[I,S]=b(null),L=I&&I.panel===N&&w.rows.some(e=>e.name===I.name)?I:null,D=o&&w.buckets.length>1&&!L,M=`${N}-rows`,A=e=>`${N}-tab-${e.toLowerCase()}`;/* @__PURE__ */
45
+ return g("div",{className:"es-insp",children:[D?/* @__PURE__ */k("div",{className:"es-insp-tabs",children:/* @__PURE__ */k(i,{ariaLabel:p??y.settings,activeId:j,onSelect:e=>{const n=e;void 0===r&&O(n),s?.(n)},items:w.buckets.map(e=>({id:e,label:y.buckets[e],icon:c?.[e],htmlId:A(e),panelId:M}))})}):null,
46
+ /* @__PURE__ */g("div",{className:"es-insp-rows",id:M,role:D?"tabpanel":void 0,"aria-labelledby":D&&j?A(j):void 0,children:[w.rows.map(({name:e,field:r,bucket:i})=>/* @__PURE__ */k("div",{className:"es-insp-row","data-field":e,"data-bucket":i,hidden:!!(L?L.name!==e:D&&i!==j)||void 0,children:/* @__PURE__ */k(E,{field:r,name:e,id:`${N}-${e}`,value:n[e],onChange:n=>t(e,n),readOnly:l,context:a,renderers:u,openItem:L&&L.name===e?L.index:null,onOpenItem:n=>S(null===n?null:{panel:N,name:e,index:n})})},e)),L?null:f]})]})}export{O as FieldNumber,j as FieldSelect,I as FieldText,H as Inspector,E as InspectorField,F as ListField,W as bucketsOf,N as clampNumber,M as copyItem,S as insertAt,P as labelOf,L as moveTo,$ as optionFor,w as parseTyped,U as planFields,D as removeAt};
@@ -0,0 +1,196 @@
1
+ /*
2
+ * efficient-easel — gold-layer styles (card 66088).
3
+ *
4
+ * The values mirror `tokens.ts` one-for-one and are declared ON `.es-gold`, not
5
+ * on `:root`, so importing this sheet themes only the boxes and never leaks a
6
+ * variable into the page being edited.
7
+ *
8
+ * THE ONE RULE THIS FILE EXISTS TO ENFORCE: the layer and the box must resolve
9
+ * to the SAME type metrics. Everything that can move a glyph — family, size,
10
+ * weight, style, line-height, letter-spacing, word-spacing, wrapping, padding —
11
+ * is inherited by both from whatever the host renders them inside, and is set
12
+ * identically on both below. A property added to one of them and not the other
13
+ * pulls the two surfaces apart, and the caret stops sitting under the character
14
+ * it is next to. There is no third place to change it.
15
+ *
16
+ * Import once from a client entry / global stylesheet:
17
+ * import 'efficient-easel/gold.css';
18
+ */
19
+
20
+ .es-gold {
21
+ /* — tokens (see tokens.ts / goldCssVars) — */
22
+ --es-gold-accent: var(--gold, #c9a461);
23
+ --es-gold-quiet: var(--es-muted, #6b7280);
24
+ --es-gold-caret: var(--es-text, #111827);
25
+ --es-gold-placeholder: var(--es-field-placeholder, #9ca3af);
26
+ --es-gold-delim-opacity: 0.35;
27
+ --es-gold-delim-resting-opacity: 0.25;
28
+ --es-gold-bold-shadow: 0 0 0.4px currentColor;
29
+ --es-gold-italic-decoration: underline dotted;
30
+ --es-gold-strike-decoration: line-through;
31
+ /**
32
+ * The field's inner inset. ZERO here and set by the host — but set on the
33
+ * THREE SURFACES below, never on this wrapper, and the difference is a real
34
+ * caret bug (2026-09-26). `.es-gold-input` is `position:absolute; inset:0`,
35
+ * which resolves to this element's PADDING box, while the layer is in flow
36
+ * and starts at its CONTENT box. Padding this element therefore moves the
37
+ * glyphs and leaves the caret behind. A host that wants an inset sets this
38
+ * variable; it must not pad `.es-gold`.
39
+ */
40
+ --es-gold-pad: 0;
41
+
42
+ position: relative;
43
+ display: block;
44
+ }
45
+
46
+ /* The shared type contract. Read the note at the top before touching it. */
47
+ .es-gold .es-gold-layer,
48
+ .es-gold .es-gold-input,
49
+ .es-gold .es-gold-placeholder {
50
+ font: inherit;
51
+ line-height: inherit;
52
+ letter-spacing: inherit;
53
+ word-spacing: inherit;
54
+ text-align: inherit;
55
+ text-indent: inherit;
56
+ text-transform: inherit;
57
+ tab-size: inherit;
58
+ white-space: pre-wrap;
59
+ overflow-wrap: break-word;
60
+ word-break: normal;
61
+ margin: 0;
62
+ /* ON ALL THREE, FROM ONE VALUE — see `--es-gold-pad`. This is the same rule
63
+ as the properties above it: anything that can move a glyph is set
64
+ identically on the layer and the box, or the caret stops sitting under
65
+ the character it is next to. */
66
+ padding: var(--es-gold-pad);
67
+ border: 0;
68
+ }
69
+
70
+ /*
71
+ * The layer is the element IN FLOW: it holds the same characters as the box, so
72
+ * it wraps to the same height, so the box grows with the text and nothing has to
73
+ * be measured.
74
+ */
75
+ .es-gold .es-gold-layer {
76
+ position: relative;
77
+ color: inherit;
78
+ pointer-events: none;
79
+ overflow: hidden;
80
+ }
81
+
82
+ /*
83
+ * A zero-width space, drawn by CSS so it is NOT part of `textContent` and cannot
84
+ * break the character-for-character promise. It buys two things: an empty layer
85
+ * still has a line box (so an empty field is one line tall, not zero), and a
86
+ * value ending in a newline still shows that last, empty line — which a trailing
87
+ * "\n" in a text node does not.
88
+ */
89
+ .es-gold .es-gold-layer::after {
90
+ content: '\200b';
91
+ }
92
+
93
+ .es-gold .es-gold-input {
94
+ position: absolute;
95
+ inset: 0;
96
+ width: 100%;
97
+ height: 100%;
98
+ display: block;
99
+ background: transparent;
100
+ /* The text is invisible; the layer underneath is what you read. */
101
+ color: transparent;
102
+ caret-color: var(--es-gold-caret);
103
+ resize: none;
104
+ overflow: hidden;
105
+ outline: none;
106
+ }
107
+
108
+ /* The hint is painted by the sibling below, in the layer's own metrics. Drawing
109
+ * it here as well would double it. */
110
+ .es-gold .es-gold-input::placeholder {
111
+ color: transparent;
112
+ }
113
+
114
+ .es-gold .es-gold-placeholder {
115
+ position: absolute;
116
+ inset: 0;
117
+ color: var(--es-gold-placeholder);
118
+ pointer-events: none;
119
+ }
120
+
121
+ /* — the marks — */
122
+
123
+ /*
124
+ * A whisper at rest, louder while you are IN the box (cards 8802.h, 8807).
125
+ *
126
+ * A merchant who is not typing should not READ `**` and `==` sitting inside
127
+ * their own page text — it looks like a bug. 8802.h took them to 0 for that,
128
+ * which worked, and left a second problem: the characters keep their full width
129
+ * either way (see below), so at 0 the space stayed and the explanation went. A
130
+ * short, heavily marked string spends about a quarter of its line on blanks
131
+ * nothing accounts for. At 0 that gap reads as a rendering bug; at a whisper it
132
+ * reads as punctuation. While the caret is in here the marks are what is being
133
+ * aimed at, so `:focus-within` brings them up to the louder dim.
134
+ *
135
+ * FADED, never removed, and done here rather than in React for the same reason.
136
+ * The characters stay in the layer at their full width: dropping a `**` would
137
+ * shift every following glyph two columns off its twin in the transparent box on
138
+ * top, and hiding them for real needs a second text engine. Opacity moves
139
+ * nothing, which is the only reason this is allowed at all — and it needs no
140
+ * state, so focusing the box does not re-render it for a change that is paint.
141
+ */
142
+ .es-gold .es-gold-delim {
143
+ opacity: var(--es-gold-delim-resting-opacity);
144
+ }
145
+
146
+ .es-gold:focus-within .es-gold-delim {
147
+ opacity: var(--es-gold-delim-opacity);
148
+ }
149
+
150
+ .es-gold .es-gold-accent {
151
+ color: var(--es-gold-accent);
152
+ font-style: normal;
153
+ }
154
+
155
+ /* Quiet is SMALLER on the page; here it is only softer, for the same reason
156
+ * bold is not heavier — a smaller glyph changes its advance. */
157
+ .es-gold .es-gold-quiet {
158
+ color: var(--es-gold-quiet);
159
+ }
160
+
161
+ /* Weight and slant are NOT applied here — see tokens.ts. Both would change a
162
+ * glyph's advance, and the transparent box on top would wrap somewhere else. */
163
+ .es-gold .es-gold-bold {
164
+ /* `<strong>`'s own bold, undone — the shadow is what draws it instead. */
165
+ font-weight: inherit;
166
+ text-shadow: var(--es-gold-bold-shadow);
167
+ }
168
+
169
+ .es-gold .es-gold-italic {
170
+ font-style: normal;
171
+ text-decoration: var(--es-gold-italic-decoration);
172
+ }
173
+
174
+ .es-gold .es-gold-strike {
175
+ text-decoration: var(--es-gold-strike-decoration);
176
+ }
177
+
178
+ /*
179
+ * Read-only (a sent campaign, a locked page). The box cannot be typed in and
180
+ * says so itself; the LAYER keeps painting, because a merchant reading a sent
181
+ * campaign should see their own formatting rather than raw delimiters.
182
+ *
183
+ * The caret is taken away rather than dimmed — there is nothing to aim.
184
+ */
185
+ .es-gold.es-gold-is-off {
186
+ --es-gold-caret: transparent;
187
+ }
188
+
189
+ .es-gold.es-gold-is-off .es-gold-layer,
190
+ .es-gold.es-gold-is-off .es-gold-placeholder {
191
+ opacity: 0.6;
192
+ }
193
+
194
+ .es-gold.es-gold-is-off .es-gold-input {
195
+ cursor: default;
196
+ }