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,280 @@
1
+ import * as react from 'react';
2
+ import { Ref } from 'react';
3
+ import { InlineRule, InlineKind } from '../markup/index.js';
4
+
5
+ /**
6
+ * The type-in-place box: a controlled leaf, and NOTHING else.
7
+ *
8
+ * It holds no string of its own, knows no document, and performs no write. The
9
+ * host owns the value and decides what a commit means — which is what lets the
10
+ * same component serve the storefront editor and the campaign designer without
11
+ * either one leaking into it.
12
+ */
13
+ interface GoldTextInputProps {
14
+ /** The authored string, delimiters and all. The single source of truth. */
15
+ value: string;
16
+ /**
17
+ * Every keystroke. The layer repaints from what the host sends back, so a host
18
+ * that drops this on the floor gets a box that does not type — deliberately:
19
+ * one value, one owner.
20
+ */
21
+ onInput: (value: string) => void;
22
+ /**
23
+ * The edit is finished and may be written: on blur, and on Enter when
24
+ * {@link multiline} is false. Fires ONCE per editing session, and never at all
25
+ * if nothing was typed — clicking into a part and back out must not dirty the
26
+ * page (owner ruling 6), and one session is one undo step (ruling 2).
27
+ */
28
+ onCommit?: (value: string) => void;
29
+ /**
30
+ * Escape. The argument is the value the component was given when it gained
31
+ * focus; the HOST puts it back, because the host owns the string. After a
32
+ * cancel the pending edit is forgotten, so the blur that follows writes
33
+ * nothing.
34
+ */
35
+ onCancel?: (restored: string) => void;
36
+ /**
37
+ * What Enter means — NOT how the text wraps. Both modes soft-wrap exactly as
38
+ * the box does, because the layer under it has to wrap identically.
39
+ *
40
+ * - `false` (default): Enter commits and never inserts a newline;
41
+ * - `true`: Enter inserts a newline and commits nothing.
42
+ */
43
+ multiline?: boolean;
44
+ /** Shown while the value is empty — through the first focus, and away on the
45
+ * first keystroke. */
46
+ placeholder?: string;
47
+ /** Extra class(es) merged onto the wrapper. */
48
+ className?: string;
49
+ /**
50
+ * The grammar's rule set — `STOREFRONT_MARKUP` in the shop,
51
+ * `CAMPAIGN_MARKUP` in the campaign designer, both from
52
+ * `efficient-easel/markup`. Defaults to `STANDARD_MARKUP`, which is the subset
53
+ * every product shares; a product with legacy runs (the storefront's
54
+ * `*phrase*` accent) MUST pass its own set or those runs draw as plain text
55
+ * here while the page paints them gold.
56
+ */
57
+ rules?: readonly InlineRule[];
58
+ /** Accessible name for the box. In-place editing has no visible label. */
59
+ ariaLabel?: string;
60
+ /**
61
+ * Nothing may be typed. The box goes out of the tab order and announces the
62
+ * state itself, rather than a parent dimming a control that is still
63
+ * focusable — the regression the 2026-08-06 audit closed.
64
+ *
65
+ * The LAYER still paints, deliberately: a sent campaign opens read-only, and
66
+ * a merchant reading it should see their own formatting, not raw delimiters.
67
+ */
68
+ disabled?: boolean;
69
+ /**
70
+ * A handle on the textarea.
71
+ *
72
+ * A plain prop rather than `ref`, for the same reason {@link GoldLayerProps}
73
+ * gives for `layerRef`: React 18 would need `forwardRef` and React 19 takes
74
+ * `ref` as an ordinary prop, and this package supports both.
75
+ *
76
+ * It exists because a caret operation — a mark button, an inserted merge tag —
77
+ * has to read the live selection off this element and put it back after the
78
+ * repaint. `efficient-easel/copy` is the caller that does that.
79
+ */
80
+ inputRef?: Ref<HTMLTextAreaElement>;
81
+ }
82
+ interface GoldLayerProps {
83
+ value: string;
84
+ rules?: readonly InlineRule[];
85
+ className?: string;
86
+ /**
87
+ * A handle on the layer element.
88
+ *
89
+ * A plain prop rather than `ref` on purpose: React 18 would need
90
+ * `forwardRef` and React 19 takes `ref` as an ordinary prop, and this package
91
+ * supports both (`peerDependencies: react >=18`). One named prop behaves
92
+ * identically on either and needs no version branch.
93
+ */
94
+ layerRef?: Ref<HTMLDivElement>;
95
+ }
96
+
97
+ /**
98
+ * Type where the text sits, and watch the gold appear as you type.
99
+ *
100
+ * The arrangement, in one paragraph: a textarea whose own text is TRANSPARENT
101
+ * sits on top of a layer holding the same characters, painted. What you read is
102
+ * the layer; what the caret walks is the box. Both take their type styles from
103
+ * whatever the host renders them inside (`font: inherit` all the way down), so
104
+ * the glyphs land on top of each other instead of near each other, and the box
105
+ * inherits the page's own type rather than a size this package invented.
106
+ *
107
+ * The layer is the element IN FLOW and the box is absolutely positioned over it,
108
+ * which is deliberate: the layer holds the same characters, so it wraps to the
109
+ * same height, so the box grows as the merchant types without anyone measuring
110
+ * anything.
111
+ *
112
+ * WHAT IT DOES NOT DO: write. It has no document, no section, no Puck, no shop.
113
+ * `onCommit` says "this edit is finished"; what that means is the host's
114
+ * business. That is what makes it shareable by both editors (constitution E2)
115
+ * and testable without any of them.
116
+ *
117
+ * No `'use client'` — same reason as the rail: the directive is the HOST's to
118
+ * place, and both editors' chrome are already client components. Unlike the
119
+ * rail, though, this leaf has hooks, so it must be rendered inside that client
120
+ * boundary; a server component can import the module but cannot render it.
121
+ *
122
+ * Requires `efficient-easel/gold.css`, imported once from a client entry.
123
+ */
124
+ declare function GoldTextInput({ value, onInput, onCommit, onCancel, multiline, placeholder, className, rules, ariaLabel, disabled, inputRef, }: GoldTextInputProps): react.JSX.Element;
125
+
126
+ /**
127
+ * The formatted text, drawn UNDER the box.
128
+ *
129
+ * It is a MARKER layer, not a preview of the page. That distinction is the whole
130
+ * design, and it is why a bold run here is not drawn at weight 600:
131
+ *
132
+ * the caret, the selection and the line breaks all come from the transparent
133
+ * textarea on top, which has ONE font. A heavier or slanted face down here is
134
+ * wider, so a marked line would wrap a word earlier than the box does and
135
+ * every line after it would sit on top of the wrong text.
136
+ *
137
+ * So the layer may only paint what cannot move a glyph — colour, opacity,
138
+ * text-decoration, a shadow. A merchant sees WHERE the formatting starts and
139
+ * ends and in which colour it will land; the real weight and slant appear on the
140
+ * page the moment they click away. Full WYSIWYG (asterisks gone, real faces) is
141
+ * a second text engine and is deliberately out of scope — see CLAUDE.md.
142
+ *
143
+ * The elements mirror the storefront's own renderer (`goldPhrases.tsx`):
144
+ * `<strong>` for bold, `<em>` for italic and for the gold accent, `<s>` for
145
+ * struck-out. The classes carry the paint; the tokens carry the values.
146
+ */
147
+ declare function GoldLayer({ value, rules, className, layerRef }: GoldLayerProps): react.JSX.Element;
148
+
149
+ /**
150
+ * Cutting an authored string into the pieces the layer paints — WITHOUT losing a
151
+ * character.
152
+ *
153
+ * The grammar is not re-implemented here and never will be (constitution E8):
154
+ * {@link tokenizeInline} from `efficient-easel/markup` (../markup) decides what is a run and
155
+ * what is not, exactly as it does for the published page. What it does not
156
+ * return is the delimiters — it hands back a run's INNER text, because every
157
+ * other consumer wants the words without the asterisks.
158
+ *
159
+ * This layer wants the asterisks. It draws underneath a transparent box whose
160
+ * caret walks the stored string, so the two surfaces must hold the same
161
+ * characters in the same order: drop the `**` and every glyph after it sits two
162
+ * columns left of the caret that is supposed to be inside it.
163
+ *
164
+ * So each parsed run is located back in the source and cut into three pieces —
165
+ * opening delimiter, inner text, closing delimiter — and EVERY piece is sliced
166
+ * out of the original string rather than rebuilt from the token. Nothing is
167
+ * retyped, so nothing can be retyped wrong.
168
+ */
169
+ /** A piece of the source: a parsed run's kind, or the delimiters around one. */
170
+ type GoldSegmentKind = InlineKind | 'delimiter';
171
+ interface GoldSegment {
172
+ kind: GoldSegmentKind;
173
+ /** Verbatim source text. Concatenating every segment reproduces the input. */
174
+ text: string;
175
+ }
176
+ /**
177
+ * Split `value` into paint-able segments under `rules`.
178
+ *
179
+ * GUARANTEE: `segments.map(s => s.text).join('') === value`, always. If the walk
180
+ * below cannot line a run up with the rule that produced it — which would mean
181
+ * the grammar and this function disagree — the whole string comes back as one
182
+ * plain `text` segment. Losing the gold is a disappointment; losing a character
183
+ * puts the caret in the wrong place, so the fallback is never in doubt.
184
+ */
185
+ declare function goldSegments(value: string, rules: readonly InlineRule[]): GoldSegment[];
186
+
187
+ /**
188
+ * Gold-layer paint tokens.
189
+ *
190
+ * SHORT LIST BY DESIGN. The layer draws inside whatever the host renders it in
191
+ * and inherits that context's type entirely — family, size, weight, line-height,
192
+ * letter-spacing — because it has to sit on top of a box that inherits the same
193
+ * (see `GoldTextInput`). So there is no type here to tokenise; what is left is
194
+ * the paint, and only the paint that cannot move a glyph.
195
+ *
196
+ * NOTHING HERE IS INVENTED (CLAUDE.md — "the tokens are not ours to invent"):
197
+ * - the gold defers to the page's own `--gold`, falling back to the
198
+ * storefront's light-theme value (`efficient-shop/app/globals.css` → `--gold:
199
+ * #c9a461`), so the mark in the box matches the mark on the page;
200
+ * - the caret and the placeholder defer to the shell layer's `--es-text` /
201
+ * `--es-field-placeholder` (see `shell/tokens.ts`) with the same values as
202
+ * their fallbacks;
203
+ * - the FOCUSED dim is the rail's existing disabled opacity (0.35), not a new
204
+ * number. Its RESTING sibling (0.25) is the one value in this file picked by
205
+ * eye rather than borrowed: nothing else in the product paints a mark that
206
+ * has to hold its width while it stops being read, so there was nothing to
207
+ * copy. The owner chose it against a picker rendering the candidates at
208
+ * heading and body size; 0.18 was tried there first and still read too
209
+ * faint (card 8807). That history is here on purpose: a later reader who
210
+ * finds 0.25 with nothing behind it will "fix" it to match some other
211
+ * number, and this is the paragraph that stops them.
212
+ *
213
+ * Every value is a STRING, unlike the rail's and shell's pixel magnitudes:
214
+ * none of these is a length, so nothing here gains a unit on the way into CSS.
215
+ *
216
+ * Exposed BOTH ways, per the same value: `goldTokens` (this object) and
217
+ * `gold.css` (the stylesheet, which declares them on `.es-gold` — names in
218
+ * {@link goldCssVars}). The parity gate in `tests/gold-leaf-safety.test.ts`
219
+ * proves the two never drift.
220
+ */
221
+ declare const goldTokens: {
222
+ /** The mark colour — `==phrase==` and the legacy `*phrase*`. */
223
+ readonly accent: "var(--gold, #c9a461)";
224
+ /** Quiet — `^^phrase^^`. Colour only: the page draws it smaller, but a
225
+ * smaller glyph down here would pull the layer off the caret. */
226
+ readonly quiet: "var(--es-muted, #6b7280)";
227
+ /** The caret. Set explicitly because the box's own text is transparent, and
228
+ * `caret-color: auto` would make the caret transparent with it. */
229
+ readonly caret: "var(--es-text, #111827)";
230
+ /** The hint shown while the value is empty. */
231
+ readonly placeholder: "var(--es-field-placeholder, #9ca3af)";
232
+ /** How far the asterisks are dimmed WHILE THE BOX HAS FOCUS — the moment they
233
+ * are what the merchant is aiming at, so this is the louder of the pair.
234
+ * Out of focus they drop to {@link goldTokens.delimiterRestingOpacity}. */
235
+ readonly delimiterOpacity: "0.35";
236
+ /** How far they are dimmed AT REST, when nobody is typing (card 8807). A
237
+ * whisper, and deliberately not 0.
238
+ *
239
+ * FADED, never removed: the characters stay in the layer at their full width
240
+ * in BOTH states, which is the deal — anything that took them out would move
241
+ * a glyph. So the width is spent either way, and 0 spends it with nothing to
242
+ * show for it: a short, heavily marked string loses about a quarter of its
243
+ * line to blanks that read as a rendering bug. Faint, the same space reads as
244
+ * punctuation. */
245
+ readonly delimiterRestingOpacity: "0.25";
246
+ /**
247
+ * Faux-bold. A real `font-weight: 600` is wider, and a wider run down here
248
+ * wraps a line earlier than the transparent box on top of it — after which
249
+ * every following line is drawn over the wrong text. The shadow reads heavier
250
+ * and moves nothing.
251
+ */
252
+ readonly boldShadow: "0 0 0.4px currentColor";
253
+ /** Italic, marked rather than slanted — an italic FACE has its own widths, and
254
+ * the layer may not change a glyph's advance. Same reason as the bold. */
255
+ readonly italicDecoration: "underline dotted";
256
+ /** Strike-through is metric-safe, so it is drawn exactly as it will print. */
257
+ readonly strikeDecoration: "line-through";
258
+ /**
259
+ * The field's inner inset, applied to the LAYER, the BOX and the
260
+ * PLACEHOLDER together — never to the wrapper.
261
+ *
262
+ * ⚠️ ZERO IS THE ONLY SAFE DEFAULT AND A HOST MUST NOT PAD `.es-gold`.
263
+ * The box is `position: absolute; inset: 0`, which resolves to the
264
+ * wrapper's PADDING box, while the layer is in flow and starts at its
265
+ * CONTENT box — so padding the wrapper moves the glyphs and leaves the
266
+ * caret behind. That shipped: `.es-copy-box` padded the wrapper and every
267
+ * character sat 6px right of its caret and 4px down (reported 2026-09-26).
268
+ * A host that wants an inset sets this token.
269
+ */
270
+ readonly pad: "0";
271
+ };
272
+ type GoldTokens = typeof goldTokens;
273
+ /**
274
+ * The CSS custom-property name behind each token. `gold.css` sets these on
275
+ * `.es-gold`, so overriding one on (or above) the box re-themes it without
276
+ * shipping new CSS — e.g. `style={{ ['--es-gold-accent']: brand }}`.
277
+ */
278
+ declare const goldCssVars: Record<keyof GoldTokens, string>;
279
+
280
+ export { GoldLayer, type GoldLayerProps, type GoldSegment, type GoldSegmentKind, GoldTextInput, type GoldTextInputProps, type GoldTokens, goldCssVars, goldSegments, goldTokens };
@@ -0,0 +1 @@
1
+ export{GoldLayer,GoldTextInput,goldSegments}from"../chunk-CERTFXCV.js";import"../chunk-6OPSJGAI.js";var e={accent:"var(--gold, #c9a461)",quiet:"var(--es-muted, #6b7280)",caret:"var(--es-text, #111827)",placeholder:"var(--es-field-placeholder, #9ca3af)",delimiterOpacity:"0.35",delimiterRestingOpacity:"0.25",boldShadow:"0 0 0.4px currentColor",italicDecoration:"underline dotted",strikeDecoration:"line-through",pad:"0"},t={accent:"--es-gold-accent",quiet:"--es-gold-quiet",caret:"--es-gold-caret",placeholder:"--es-gold-placeholder",delimiterOpacity:"--es-gold-delim-opacity",delimiterRestingOpacity:"--es-gold-delim-resting-opacity",boldShadow:"--es-gold-bold-shadow",italicDecoration:"--es-gold-italic-decoration",strikeDecoration:"--es-gold-strike-decoration",pad:"--es-gold-pad"};export{t as goldCssVars,e as goldTokens};