@snowcone-app/canvas 0.20.0 → 0.21.1

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 (45) hide show
  1. package/README.md +129 -13
  2. package/STYLING_CONTRACT.md +427 -0
  3. package/dist/{EditorCanvasImpl-UBKdG0c0.js → EditorCanvasImpl-2ZgIUVpK.js} +4 -4
  4. package/dist/{EditorCanvasImpl-UBKdG0c0.js.map → EditorCanvasImpl-2ZgIUVpK.js.map} +1 -1
  5. package/dist/{SnowconeCanvas-B01o5LmS.js → SnowconeCanvas-DqZicdxK.js} +2 -2
  6. package/dist/{SnowconeCanvas-B01o5LmS.js.map → SnowconeCanvas-DqZicdxK.js.map} +1 -1
  7. package/dist/UnifiedFontService-B1PGVu8O.js.map +1 -1
  8. package/dist/components/editor/EditorAdd.d.ts +2 -1
  9. package/dist/components/editor/EditorBuy.d.ts +2 -1
  10. package/dist/components/editor/EditorCanvas.d.ts +2 -1
  11. package/dist/components/editor/EditorControls.d.ts +2 -1
  12. package/dist/components/editor/EditorHistory.d.ts +2 -1
  13. package/dist/components/editor/EditorLayers.d.ts +2 -1
  14. package/dist/components/editor/EditorMenu.d.ts +2 -1
  15. package/dist/components/editor/EditorPreview.d.ts +2 -1
  16. package/dist/components/editor/EditorRoot.d.ts +2 -1
  17. package/dist/components/editor/EditorTextField.d.ts +2 -1
  18. package/dist/components/editor/useProductSelection.d.ts +2 -1
  19. package/dist/components/embed-realtime/context.d.ts +1 -1
  20. package/dist/contexts/EditorContext.d.ts +1 -1
  21. package/dist/default-editor/SnowconeEditor.d.ts +2 -1
  22. package/dist/design/design-builder.d.ts +2 -2
  23. package/dist/{design-builder-DMKx9UXD.js → design-builder-CPF60RU2.js} +1 -2
  24. package/dist/design-builder-CPF60RU2.js.map +1 -0
  25. package/dist/editorErrors-CK1RISFa.js.map +1 -1
  26. package/dist/{embed-uALcVSYN.js → embed-2tp6Ue90.js} +4 -4
  27. package/dist/embed-2tp6Ue90.js.map +1 -0
  28. package/dist/embed.mjs +3 -3
  29. package/dist/index.mjs +3 -3
  30. package/dist/{template-fields-B9RagVpT.js → template-fields-FgImixyv.js} +2 -2
  31. package/dist/{template-fields-B9RagVpT.js.map → template-fields-FgImixyv.js.map} +1 -1
  32. package/dist/testing/assertRenders.d.ts +2 -1
  33. package/dist/testing.mjs +1 -1
  34. package/dist/testing.mjs.map +1 -1
  35. package/example/README.md +179 -0
  36. package/example/index.html +12 -0
  37. package/example/package.json +24 -0
  38. package/example/src/Widget.tsx +65 -0
  39. package/example/src/main.tsx +9 -0
  40. package/example/tsconfig.json +14 -0
  41. package/example/vite.config.ts +11 -0
  42. package/llms.txt +45 -0
  43. package/package.json +6 -2
  44. package/dist/design-builder-DMKx9UXD.js.map +0 -1
  45. package/dist/embed-uALcVSYN.js.map +0 -1
package/README.md CHANGED
@@ -1,14 +1,24 @@
1
1
  # @snowcone-app/canvas
2
2
 
3
- > **Building with an AI agent?** Read the complete agent-facing docs in one fetch — <https://developers.snowcone.app/llms-full.txt> — before integrating. It's the source of truth; grep it before assuming a field or behavior is undocumented.
4
-
5
- **Embeddable design editor for print-on-demand and product customization.**
6
-
7
- `<SnowconeCanvas />` is a self-contained React component that lets your shoppers
8
- compose the artwork that goes onto a product — text, images, transforms, and
9
- effects across one or more print areas. It's the same editor that powers Edit and
10
- Remix on [snowcone.app](https://snowcone.app). Drop it in, get the design state and
11
- print-ready exports back out.
3
+ > **Building with an AI agent?** This tarball is self-sufficient: start with
4
+ > [`llms.txt`](./llms.txt) (the agent index), copy the canonical example in
5
+ > [`example/`](./example/README.md), and restyle via the generated
6
+ > [`STYLING_CONTRACT.md`](./STYLING_CONTRACT.md). The complete hosted docs are
7
+ > one fetch — <https://developers.snowcone.app/llms-full.txt> — grep them
8
+ > before assuming a field or behavior is undocumented.
9
+
10
+ **Embeddable product personalization and design editing for e-commerce.**
11
+
12
+ Two surfaces, one package:
13
+
14
+ 1. **`Editor.*` composable parts** (`@snowcone-app/canvas/embed`) — live
15
+ server-rendered product previews, layer-bound form fields, and smart
16
+ add-to-cart, composed in your own layout. The turnkey `SnowconeEditor` is
17
+ the same parts in our arrangement.
18
+ 2. **`SnowconeCanvas`** (`@snowcone-app/canvas`) — the standalone design
19
+ editor: text, images, transforms, and effects across one or more print
20
+ areas. The same editor that powers Edit and Remix on
21
+ [snowcone.app](https://snowcone.app).
12
22
 
13
23
  📖 **Full documentation: [developers.snowcone.app/canvas](https://developers.snowcone.app/canvas)**
14
24
 
@@ -21,7 +31,115 @@ npm install @snowcone-app/canvas
21
31
 
22
32
  `react` and `react-dom` (>=18) are peer dependencies.
23
33
 
24
- ## Quick start
34
+ ## The canonical example — a personalization widget, no canvas
35
+
36
+ The fastest path to a working integration. A live product preview, one
37
+ personalized text layer, and a completeness-gated add-to-cart — the full
38
+ design editor never loads. This file ships in this package at
39
+ [`example/src/Widget.tsx`](./example/src/Widget.tsx) (reading this in the
40
+ monorepo instead of the npm tarball? It lives at
41
+ `packages/canvas/fixtures/no-canvas-widget/` there — `example/` is assembled at
42
+ publish); copy it and change the design, the product ids, and the parts.
43
+
44
+ <!-- canonical-example (verbatim from example/src/Widget.tsx; drift-gated) -->
45
+
46
+ ```tsx
47
+ import React from 'react';
48
+ import { Editor, design, type EditorBuyPayload } from '@snowcone-app/canvas/embed';
49
+
50
+ const SHOP_KEY = import.meta.env.VITE_SNOWCONE_SHOP ?? 'demo-shop';
51
+
52
+ // The design is the single source of truth: it seeds the first render AND
53
+ // mints the typed layer tokens the TextField binds with.
54
+ const nameDrop = design({ name: 'Front', width: 1480, height: 2328 })
55
+ .background('https://dqMwU8Jct3.storage.snowcone.app/demo/demo-008.jpg', {
56
+ aspectRatio: 1480 / 2328,
57
+ name: 'art',
58
+ })
59
+ .text('YOUR NAME', { anchor: 'top', name: 'name' });
60
+
61
+ const product = {
62
+ productId: 'BEEB77',
63
+ mockupIds: ['FV1qjO'],
64
+ placements: ['Front'],
65
+ // BEEB77 has a COLOR axis (Frame) — a color product REQUIRES variantId, or
66
+ // a live render times out. Read real ids from the catalog with getProduct().
67
+ variantId: 'Pv1sLC',
68
+ };
69
+
70
+ export function PersonalizationWidget({
71
+ onAddToCart,
72
+ }: {
73
+ /** Your cart handler. NOTE the payload's `designState` is the EDITOR shape
74
+ * (flat `elements` + artboard metadata) — persist it as-is. It is NOT the
75
+ * server render shape (`artboards[].elements`) the session pushes;
76
+ * `payload.serverRequest` carries that one if you need it. */
77
+ onAddToCart?: (payload: EditorBuyPayload) => void;
78
+ } = {}): React.ReactElement {
79
+ return (
80
+ <Editor.Root shop={SHOP_KEY} product={product} design={nameDrop} grantUrl="/api/realtime/grant">
81
+ <div style={{ display: 'grid', gap: 12, maxWidth: 320 }}>
82
+ <Editor.Preview alt="Your personalized tee" fallback={<div aria-busy="true">Rendering…</div>} />
83
+ <Editor.TextField layer={nameDrop.layer('name')} placeholder="Your name" />
84
+ <Editor.Buy
85
+ onAddToCart={
86
+ onAddToCart ??
87
+ (({ productId, quantity, designState }) => {
88
+ // The host's one line: persist the designState (the
89
+ // re-renderable source of truth) with the cart line.
90
+ console.log('add-to-cart', { productId, quantity, designState });
91
+ })
92
+ }
93
+ >
94
+ Add to cart · $24
95
+ </Editor.Buy>
96
+ </div>
97
+ </Editor.Root>
98
+ );
99
+ }
100
+ ```
101
+
102
+ Don't forget the stylesheet (once, anywhere in your app):
103
+
104
+ ```tsx
105
+ import '@snowcone-app/canvas/style.css';
106
+ ```
107
+
108
+ `grantUrl` points at a small route on YOUR server that mints a render grant
109
+ (your keys never reach the browser). Framework variants (Next.js, Remix,
110
+ Express) are in the docs: <https://developers.snowcone.app/primitives>.
111
+
112
+ ### The parts
113
+
114
+ All from `@snowcone-app/canvas/embed`, all valid in any subset under one
115
+ `Editor.Root` — the host owns the layout:
116
+
117
+ | Part | What it carries |
118
+ |------|-----------------|
119
+ | `Editor.Root` | session, design state, error boundary — the one provider |
120
+ | `Editor.Preview` | live product mockup (×N under one Root is valid) |
121
+ | `Editor.TextField` | input bound to a design text layer by typed token |
122
+ | `Editor.Buy` | completeness gating + cart payload; submit is your callback |
123
+ | `Editor.Canvas` | the editable artboard (the one lazy-loaded heavy chunk) |
124
+ | `Editor.Layers` / `Editor.Controls` / `Editor.History` / `Editor.Menu` / `Editor.Add` | editor chrome as parts |
125
+
126
+ The turnkey default — the same parts in our arrangement:
127
+
128
+ ```tsx
129
+ import { SnowconeEditor } from '@snowcone-app/canvas/embed';
130
+
131
+ <SnowconeEditor shop={SHOP_KEY} product={product} design={d} grantUrl="/api/realtime/grant" onAddToCart={addLine} />
132
+ ```
133
+
134
+ Every part renders minimal DOM tagged `data-sc-part` and themes through
135
+ `.sc-root` CSS custom properties — the full generated reference is
136
+ [`STYLING_CONTRACT.md`](./STYLING_CONTRACT.md). Composition mistakes throw
137
+ structured `EditorError`s (stable `code` + `error.data`) in dev AND prod,
138
+ contained by the Root's error boundary. Test with
139
+ `@snowcone-app/canvas/testing`: `assertRenders` proves a design's bindings
140
+ are alive; `expectNoEditorErrors()` makes contained errors fail your tests.
141
+
142
+ ## The standalone design editor
25
143
 
26
144
  ```tsx
27
145
  import { SnowconeCanvas } from '@snowcone-app/canvas';
@@ -45,8 +163,6 @@ That renders a working editor out of the box (toolbar, layers, text/image/effect
45
163
  Use the `kit` prop to switch presets (`'pro-studio'` default, `'compact-customizer'`,
46
164
  `'embed-only'`) or pass `layoutConfig` to go canvas-only.
47
165
 
48
- ## Common recipes
49
-
50
166
  ### Multiple print areas (placements)
51
167
 
52
168
  ```tsx
@@ -95,7 +211,7 @@ const [state, setState] = useState<CanvasState | null>(null);
95
211
 
96
212
  Exports always produce transparent PNGs/WebP — the preview background is never baked in.
97
213
 
98
- ## API at a glance
214
+ ### API at a glance
99
215
 
100
216
  `<SnowconeCanvas />` is the single entry point. Key props:
101
217
 
@@ -0,0 +1,427 @@
1
+ # `Editor.*` styling contract
2
+
3
+ <!-- GENERATED — do not edit. Derived from the parts themselves by
4
+ packages/canvas/src/components/editor/__tests__/stylingContract.test.tsx.
5
+ Regenerate: pnpm -C packages/canvas gen:styling-contract -->
6
+
7
+ Every `Editor.*` part renders minimal styled DOM carrying a stable
8
+ `data-sc-part` attribute, accepts `className`/`style`, and is themed by the
9
+ `.sc-root` CSS custom properties below. Restyle by targeting
10
+ `[data-sc-part="…"]` selectors and overriding `.sc-root` tokens — you never
11
+ need this package's source. Attribute values shown are the stable enums;
12
+ `<angle-bracket>` values are runtime data. `sc-*` classes are additional
13
+ stable hooks; `sc:`-prefixed utility classes are internal and NOT contract.
14
+
15
+ ## Part index
16
+
17
+ | `data-sc-part` | rendered by |
18
+ |---|---|
19
+ | `add` | Editor.Add |
20
+ | `buy` | Editor.Buy |
21
+ | `canvas` | Editor.Canvas |
22
+ | `controls` | Editor.Controls |
23
+ | `error` | Editor.Root |
24
+ | `error-retry` | Editor.Root |
25
+ | `history` | Editor.History |
26
+ | `layers` | Editor.Layers |
27
+ | `layers-item` | Editor.Layers |
28
+ | `menu` | Editor.Menu |
29
+ | `preview` | Editor.Preview |
30
+ | `preview-error` | Editor.Preview |
31
+ | `root` | Editor.Root |
32
+ | `snowcone-editor` | SnowconeEditor |
33
+ | `snowcone-editor-header` | SnowconeEditor |
34
+ | `snowcone-editor-idle-dock` | SnowconeEditor |
35
+ | `snowcone-editor-layer-thumb` | SnowconeEditor |
36
+ | `snowcone-editor-menu` | SnowconeEditor |
37
+ | `snowcone-editor-menu-item` | SnowconeEditor |
38
+ | `snowcone-editor-menu-list` | SnowconeEditor |
39
+ | `snowcone-editor-pip` | SnowconeEditor |
40
+ | `snowcone-editor-stage` | SnowconeEditor |
41
+ | `snowcone-editor-swap-back` | SnowconeEditor |
42
+ | `snowcone-editor-swap-stage` | SnowconeEditor |
43
+ | `snowcone-editor-zone` | SnowconeEditor |
44
+ | `textfield` | Editor.TextField |
45
+
46
+ ## The DOM each part renders
47
+
48
+ ### `add` — Editor.Add
49
+
50
+ ```html
51
+ <div data-sc-part="add">
52
+ <div>
53
+ <div>
54
+ <button aria-label=<label>>
55
+ ```
56
+
57
+ ### `buy` — Editor.Buy — incomplete (seed value untouched)
58
+
59
+ ```html
60
+ <button data-sc-buy-missing=<reason-codes> data-sc-buy-state="incomplete" data-sc-part="buy" disabled type="button">
61
+ ```
62
+
63
+ ### `buy` — Editor.Buy — ready
64
+
65
+ ```html
66
+ <button data-sc-buy-state="ready" data-sc-part="buy" type="button">
67
+ ```
68
+
69
+ ### `canvas` — Editor.Canvas
70
+
71
+ ```html
72
+ <div class="sc-canvas-frame" data-sc-part="canvas">
73
+ <div class="sc-root">
74
+ <div>
75
+ <div>
76
+ <div role="status">
77
+ <canvas aria-label=<label> role="application">
78
+ ```
79
+
80
+ ### `controls` — Editor.Controls — placement="docked", element selected
81
+
82
+ ```html
83
+ <div data-sc-part="controls" data-sc-placement="docked">
84
+ <div>
85
+ <div>
86
+ <div>
87
+ <div class="sc-toolbar" data-mode="embedded">
88
+ …
89
+ <div class="sc-secondary-toolbar-panel-wrapper">
90
+ <div> ×16
91
+ ```
92
+
93
+ ### `controls` — Editor.Controls — built-in floating toolbar (no Controls mounted; body portal)
94
+
95
+ ```html
96
+ <div class="sc-toolbar" data-mode="floating" data-sc-part="controls">
97
+ <div class="sc-toolbar-slide-container">
98
+ <div class="sc-toolbar-regular-content">
99
+ <span>
100
+ <span>
101
+ <button aria-label=<label> aria-pressed="false">
102
+ <svg>
103
+ …
104
+ <div class="sc-toolbar-items">
105
+ <button aria-label=<label> title=<title>>
106
+ …
107
+ <button aria-label=<label> aria-pressed="false">
108
+ …
109
+ <button aria-label=<label> class="sc-toolbar-btn">
110
+ …
111
+ <button aria-label=<label> aria-pressed="false">
112
+ … ×5
113
+ <button aria-expanded="false" aria-label=<label> class="sc-toolbar-btn" type="button">
114
+ <button aria-label=<label> aria-pressed="false">
115
+ … ×6
116
+ <button aria-label=<label> aria-pressed="false" disabled>
117
+ …
118
+ <button aria-expanded="false" aria-label=<label> class="sc-toolbar-btn" type="button">
119
+ <div class="sc-toolbar-collapsed-header">
120
+ ```
121
+
122
+ ### `error` — Editor.Root — contained-error card (E_CHUNK_LOAD_FAILED shown)
123
+
124
+ ```html
125
+ <div data-sc-part="error" role="alert">
126
+ <p> ×2
127
+ <button data-sc-part="error-retry" type="button">
128
+ ```
129
+
130
+ ### `error-retry` — Editor.Root — the retry affordance (retryable errors only)
131
+
132
+ ```html
133
+ <button data-sc-part="error-retry" type="button">
134
+ ```
135
+
136
+ ### `history` — Editor.History
137
+
138
+ ```html
139
+ <div data-sc-part="history">
140
+ <div class="sc-undo-redo">
141
+ <button aria-label=<label> aria-pressed="false" disabled>
142
+ <span class="sc-normalized-icon-responsive"> ×2
143
+ ```
144
+
145
+ ### `layers` — Editor.Layers — with a mounted Canvas (live document panel)
146
+
147
+ ```html
148
+ <div data-sc-direction="vertical" data-sc-part="layers">
149
+ <div class="sc-layers-panel">
150
+ <div>
151
+ <span>
152
+ <div>
153
+ <div>
154
+ <div> ×2
155
+ <div>
156
+ … ×2
157
+ ```
158
+
159
+ ### `layers` — Editor.Layers — canvas-free (design layers from the store)
160
+
161
+ ```html
162
+ <div data-sc-direction="vertical" data-sc-part="layers">
163
+ <div>
164
+ <div data-sc-layer-kind="custom" data-sc-part="layers-item">
165
+ <span>
166
+ <div data-sc-layer-kind="image" data-sc-part="layers-item">
167
+ <span>
168
+ ```
169
+
170
+ ### `layers-item` — Editor.Layers — one canvas-free layer row
171
+
172
+ ```html
173
+ <div data-sc-layer-kind="custom" data-sc-part="layers-item">
174
+ <span>
175
+ ```
176
+
177
+ ### `menu` — Editor.Menu
178
+
179
+ ```html
180
+ <div data-sc-part="menu">
181
+ <div>
182
+ <div>
183
+ <button aria-label=<label>>
184
+ <input type="file">
185
+ ```
186
+
187
+ ### `preview` — Editor.Preview — with a rendered mockup
188
+
189
+ ```html
190
+ <img alt=<alt> data-sc-part="preview" src=<url>>
191
+ ```
192
+
193
+ ### `preview-error` — Editor.Preview — render/grant failure before any mockup
194
+
195
+ ```html
196
+ <div data-sc-error=<message> data-sc-part="preview-error" role="alert">
197
+ ```
198
+
199
+ ### `root` — Editor.Root
200
+
201
+ ```html
202
+ <div class="sc-root" data-sc-part="root">
203
+ <img alt=<alt> data-sc-part="preview" src=<url>>
204
+ <input aria-label=<label> data-sc-layer=<layer-name> data-sc-part="textfield" placeholder=<placeholder> type="text">
205
+ <button data-sc-buy-missing=<reason-codes> data-sc-buy-state="incomplete" data-sc-part="buy" disabled type="button">
206
+ ```
207
+
208
+ ### `snowcone-editor` — SnowconeEditor
209
+
210
+ ```html
211
+ <div data-sc-part="snowcone-editor">
212
+ <div data-sc-part="snowcone-editor-header">
213
+ <span>
214
+ <div data-sc-part="history">
215
+ <div class="sc-undo-redo">
216
+ <button aria-label=<label> aria-pressed="false" disabled>
217
+ … ×2
218
+ <div>
219
+ <button aria-expanded="false" aria-label=<label> data-sc-part="snowcone-editor-menu" type="button">
220
+ <button data-sc-buy-state="ready" data-sc-part="buy" type="button">
221
+ <div data-sc-part="snowcone-editor-stage">
222
+ <div class="sc-canvas-frame" data-sc-part="canvas">
223
+ <div class="sc-root">
224
+ <div>
225
+ …
226
+ <button aria-label=<label> data-sc-part="snowcone-editor-pip" type="button">
227
+ <span aria-busy="true">
228
+ <div data-sc-part="snowcone-editor-zone" data-sc-zone-state="idle">
229
+ <div>
230
+ <div data-sc-part="controls" data-sc-placement="docked">
231
+ <div>
232
+ …
233
+ <div data-sc-part="snowcone-editor-idle-dock">
234
+ <div data-sc-part="add">
235
+ <div>
236
+ …
237
+ <div>
238
+ <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
239
+ <span> ×2
240
+ ```
241
+
242
+ ### `snowcone-editor-header` — SnowconeEditor — header
243
+
244
+ ```html
245
+ <div data-sc-part="snowcone-editor-header">
246
+ <span>
247
+ <div data-sc-part="history">
248
+ <div class="sc-undo-redo">
249
+ <button aria-label=<label> aria-pressed="false" disabled>
250
+ <span class="sc-normalized-icon-responsive"> ×2
251
+ <div>
252
+ <button aria-expanded="false" aria-label=<label> data-sc-part="snowcone-editor-menu" type="button">
253
+ <button data-sc-buy-state="ready" data-sc-part="buy" type="button">
254
+ ```
255
+
256
+ ### `snowcone-editor-idle-dock` — SnowconeEditor — idle dock
257
+
258
+ ```html
259
+ <div data-sc-part="snowcone-editor-idle-dock">
260
+ <div data-sc-part="add">
261
+ <div>
262
+ <div>
263
+ <button aria-label=<label>>
264
+ <div>
265
+ <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
266
+ <span> ×2
267
+ ```
268
+
269
+ ### `snowcone-editor-layer-thumb` — SnowconeEditor — one layer thumbnail
270
+
271
+ ```html
272
+ <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
273
+ <span>
274
+ ```
275
+
276
+ ### `snowcone-editor-menu` — SnowconeEditor — the ⋯ menu button
277
+
278
+ ```html
279
+ <button aria-expanded="false" aria-label=<label> data-sc-part="snowcone-editor-menu" type="button">
280
+ ```
281
+
282
+ ### `snowcone-editor-menu-item` — SnowconeEditor — one ⋯ menu item
283
+
284
+ ```html
285
+ <button data-sc-part="snowcone-editor-menu-item" role="menuitem" type="button">
286
+ ```
287
+
288
+ ### `snowcone-editor-menu-list` — SnowconeEditor — open ⋯ menu
289
+
290
+ ```html
291
+ <div data-sc-part="snowcone-editor-menu-list" role="menu">
292
+ <button data-sc-part="snowcone-editor-menu-item" role="menuitem" type="button"> ×2
293
+ ```
294
+
295
+ ### `snowcone-editor-pip` — SnowconeEditor — preview PiP
296
+
297
+ ```html
298
+ <button aria-label=<label> data-sc-part="snowcone-editor-pip" type="button">
299
+ <span aria-busy="true">
300
+ ```
301
+
302
+ ### `snowcone-editor-stage` — SnowconeEditor — stage
303
+
304
+ ```html
305
+ <div data-sc-part="snowcone-editor-stage">
306
+ <div class="sc-canvas-frame" data-sc-part="canvas">
307
+ <div class="sc-root">
308
+ <div>
309
+ <div>
310
+ …
311
+ <button aria-label=<label> data-sc-part="snowcone-editor-pip" type="button">
312
+ <span aria-busy="true">
313
+ ```
314
+
315
+ ### `snowcone-editor-swap-back` — SnowconeEditor — the "artwork" chip back to the canvas
316
+
317
+ ```html
318
+ <button aria-label=<label> data-sc-part="snowcone-editor-swap-back" type="button">
319
+ ```
320
+
321
+ ### `snowcone-editor-swap-stage` — SnowconeEditor — mockup swapped onto the stage
322
+
323
+ ```html
324
+ <div data-sc-part="snowcone-editor-swap-stage">
325
+ <div aria-busy="true">
326
+ <button aria-label=<label> data-sc-part="snowcone-editor-swap-back" type="button">
327
+ ```
328
+
329
+ ### `snowcone-editor-zone` — SnowconeEditor — morphing control zone (idle)
330
+
331
+ ```html
332
+ <div data-sc-part="snowcone-editor-zone" data-sc-zone-state="idle">
333
+ <div>
334
+ <div data-sc-part="controls" data-sc-placement="docked">
335
+ <div>
336
+ <div>
337
+ <div>
338
+ …
339
+ <div class="sc-secondary-toolbar-panel-wrapper">
340
+ … ×16
341
+ <div data-sc-part="snowcone-editor-idle-dock">
342
+ <div data-sc-part="add">
343
+ <div>
344
+ <div>
345
+ …
346
+ <div>
347
+ <button aria-label=<label> data-sc-part="snowcone-editor-layer-thumb" type="button">
348
+ <span> ×2
349
+ ```
350
+
351
+ ### `textfield` — Editor.TextField
352
+
353
+ ```html
354
+ <input aria-label=<label> data-sc-layer=<layer-name> data-sc-part="textfield" placeholder=<placeholder> type="text">
355
+ ```
356
+
357
+ ## `.sc-root` theming tokens
358
+
359
+ Declared on `.sc-root` (applied once by `Editor.Root`); override any of
360
+ them on your own wrapper around the Root to retheme every part. Portaled
361
+ UI (the floating toolbar) carries its own `.sc-root` wrapper, so overrides
362
+ on `:root` or above the widget apply there too.
363
+
364
+ | group | token | default |
365
+ |---|---|---|
366
+ | Semantic Colors | `--color-primary` | `#000000` |
367
+ | Semantic Colors | `--color-primary-foreground` | `#ffffff` |
368
+ | Semantic Colors | `--color-secondary` | `#f4f4f5` |
369
+ | Semantic Colors | `--color-secondary-foreground` | `#18181b` |
370
+ | Semantic Colors | `--color-default` | `#f0f0f1` |
371
+ | Semantic Colors | `--color-default-foreground` | `#18181b` |
372
+ | Semantic Colors | `--color-danger` | `#ef4444` |
373
+ | Semantic Colors | `--color-danger-foreground` | `#ffffff` |
374
+ | Semantic Colors | `--color-success` | `#22c55e` |
375
+ | Semantic Colors | `--color-success-foreground` | `#ffffff` |
376
+ | Semantic Colors | `--color-warning` | `#f59e0b` |
377
+ | Semantic Colors | `--color-warning-foreground` | `#000000` |
378
+ | Layout Colors | `--color-background` | `#f8f8f8` |
379
+ | Layout Colors | `--color-foreground` | `#0a0a0a` |
380
+ | Layout Colors | `--color-surface` | `#ffffff` |
381
+ | Layout Colors | `--color-surface-foreground` | `#0a0a0a` |
382
+ | Layout Colors | `--color-muted` | `#f4f4f5` |
383
+ | Layout Colors | `--color-muted-foreground` | `#71717a` |
384
+ | Layout Colors | `--color-divider` | `#e4e4e7` |
385
+ | Layout Colors | `--color-focus` | `#000000` |
386
+ | Field/Input Colors | `--color-field` | `#ffffff` |
387
+ | Field/Input Colors | `--color-field-foreground` | `#0a0a0a` |
388
+ | Field/Input Colors | `--color-field-placeholder` | `#71717a` |
389
+ | Field/Input Colors | `--color-field-border` | `transparent` |
390
+ | Accent (backward compat) | `--color-accent` | `#000000` |
391
+ | Accent (backward compat) | `--color-accent-foreground` | `#ffffff` |
392
+ | Shadcn compatibility aliases | `--color-card` | `#ffffff` |
393
+ | Shadcn compatibility aliases | `--color-card-foreground` | `#0a0a0a` |
394
+ | Shadcn compatibility aliases | `--color-popover` | `#ffffff` |
395
+ | Shadcn compatibility aliases | `--color-popover-foreground` | `#0a0a0a` |
396
+ | Shadcn compatibility aliases | `--color-border` | `#e4e4e7` |
397
+ | Shadcn compatibility aliases | `--color-destructive` | `#ef4444` |
398
+ | Shadcn compatibility aliases | `--color-destructive-foreground` | `#ffffff` |
399
+ | Shadcn compatibility aliases | `--color-ring` | `#000000` |
400
+ | Shadcn compatibility aliases | `--color-input` | `transparent` |
401
+ | Overlay/Surface | `--color-overlay` | `#ffffff` |
402
+ | Overlay/Surface | `--color-overlay-foreground` | `#0a0a0a` |
403
+ | Overlay/Surface | `--shadow-overlay` | `0 8px 30px rgb(0 0 0 / 0.12)` |
404
+ | Overlay/Surface | `--shadow-surface` | `0 1px 3px rgb(0 0 0 / 0.1)` |
405
+ | Overlay/Surface | `--shadow-soft` | `0 1px 2px rgb(0 0 0 / 0.06), 0 2px 4px rgb(0 0 0 / 0.04)` |
406
+ | Semantic Font Families | `--font-family-heading` | `ui-sans-serif, system-ui, sans-serif` |
407
+ | Semantic Font Families | `--font-family-body` | `ui-sans-serif, system-ui, sans-serif` |
408
+ | Semantic Font Families | `--font-family-label` | `ui-sans-serif, system-ui, sans-serif` |
409
+ | Semantic Font Families | `--font-family-button` | `ui-sans-serif, system-ui, sans-serif` |
410
+ | Semantic Font Families | `--font-family-caption` | `ui-sans-serif, system-ui, sans-serif` |
411
+ | Semantic Font Families | `--font-family-display` | `ui-sans-serif, system-ui, sans-serif` |
412
+ | Semantic Font Families | `--font-family-accent` | `ui-sans-serif, system-ui, sans-serif` |
413
+ | Semantic Radius | `--radius-button` | `4px` |
414
+ | Semantic Radius | `--radius-input` | `4px` |
415
+ | Semantic Radius | `--radius-card` | `6px` |
416
+ | Semantic Radius | `--radius-modal` | `8px` |
417
+ | Semantic Radius | `--radius-badge` | `9999px` |
418
+ | Semantic Radius | `--radius-avatar` | `9999px` |
419
+ | Semantic Radius | `--radius-tooltip` | `6px` |
420
+ | Semantic Radius | `--radius-image` | `4px` |
421
+ | Z-index scale | `--z-base` | `1` |
422
+ | Z-index scale | `--z-sticky` | `1000` |
423
+ | Z-index scale | `--z-dropdown` | `1001` |
424
+ | Z-index scale | `--z-drawer` | `10002` |
425
+ | Z-index scale | `--z-overlay` | `10001` |
426
+ | Z-index scale | `--z-modal` | `10000` |
427
+ | Z-index scale | `--z-tooltip` | `2147483647` |
@@ -1,9 +1,9 @@
1
1
  import { jsx as m, jsxs as R, Fragment as $ } from "react/jsx-runtime";
2
2
  import { forwardRef as j, useContext as E, useSyncExternalStore as D, useMemo as G, useRef as y, useLayoutEffect as I } from "react";
3
- import { S as N } from "./SnowconeCanvas-B01o5LmS.js";
3
+ import { S as N } from "./SnowconeCanvas-DqZicdxK.js";
4
4
  import { d as U } from "./ContextualToolbars--vTtorNg.js";
5
- import { s as V } from "./design-builder-DMKx9UXD.js";
6
- import { u as k } from "./embed-uALcVSYN.js";
5
+ import { s as V } from "./design-builder-CPF60RU2.js";
6
+ import { u as k } from "./embed-2tp6Ue90.js";
7
7
  import { c as z, b as L } from "./editorErrors-CK1RISFa.js";
8
8
  import { T as O } from "./FontAnalyzer-BpsTYowD.js";
9
9
  import { p as h } from "./UnifiedFontService-B1PGVu8O.js";
@@ -79,4 +79,4 @@ const te = j(
79
79
  export {
80
80
  te as EditorCanvasImpl
81
81
  };
82
- //# sourceMappingURL=EditorCanvasImpl-UBKdG0c0.js.map
82
+ //# sourceMappingURL=EditorCanvasImpl-2ZgIUVpK.js.map