@snowcone-app/canvas 0.37.0 → 0.38.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 (69) hide show
  1. package/ERROR_CONTRACT.md +39 -8
  2. package/STYLING_CONTRACT.md +24 -2
  3. package/dist/{CanvasRenderer-CTN4j-uO.js → CanvasRenderer-D-E5WWLm.js} +4 -4
  4. package/dist/{CanvasRenderer-CTN4j-uO.js.map → CanvasRenderer-D-E5WWLm.js.map} +1 -1
  5. package/dist/{CanvasStateV1-DLE5d1-d.js → CanvasStateV1-C9LQt2J0.js} +7 -7
  6. package/dist/{CanvasStateV1-DLE5d1-d.js.map → CanvasStateV1-C9LQt2J0.js.map} +1 -1
  7. package/dist/{ContextualToolbars-CG5Gmv_i.js → ContextualToolbars-C62As-xY.js} +202 -202
  8. package/dist/{ContextualToolbars-CG5Gmv_i.js.map → ContextualToolbars-C62As-xY.js.map} +1 -1
  9. package/dist/EditorCanvasImpl-BnxEbr2p.js +144 -0
  10. package/dist/EditorCanvasImpl-BnxEbr2p.js.map +1 -0
  11. package/dist/{ExportManager-CDX5_up8.js → ExportManager-BbBfO22S.js} +45 -45
  12. package/dist/{ExportManager-CDX5_up8.js.map → ExportManager-BbBfO22S.js.map} +1 -1
  13. package/dist/{FontAnalyzer-CEYA_6fY.js → FontAnalyzer-DWHS41ba.js} +128 -128
  14. package/dist/FontAnalyzer-DWHS41ba.js.map +1 -0
  15. package/dist/{ImagePanel-DLxIDKV-.js → ImagePanel-BaX0UE-L.js} +57 -57
  16. package/dist/{ImagePanel-DLxIDKV-.js.map → ImagePanel-BaX0UE-L.js.map} +1 -1
  17. package/dist/SnowconeCanvas-CWSPyqqj.js +1112 -0
  18. package/dist/SnowconeCanvas-CWSPyqqj.js.map +1 -0
  19. package/dist/advanced.mjs +138 -138
  20. package/dist/api/placement.d.ts +3 -0
  21. package/dist/components/editor/EditorPlacements.d.ts +19 -6
  22. package/dist/components/editor/RootInternals.d.ts +7 -0
  23. package/dist/components/editor/designStateStore.d.ts +18 -0
  24. package/dist/components/editor/index.d.ts +3 -1
  25. package/dist/components/editor/placementLinks.d.ts +52 -0
  26. package/dist/components/editor/usePlacements.d.ts +28 -4
  27. package/dist/components/embed-realtime/context.d.ts +20 -4
  28. package/dist/components/embed-realtime/usePieceGuideBundle.d.ts +1 -0
  29. package/dist/components/embed-realtime/validateEmbed.d.ts +6 -3
  30. package/dist/{custom-icons-D51KIAfz.js → custom-icons-o1U13Vcg.js} +14 -14
  31. package/dist/{custom-icons-D51KIAfz.js.map → custom-icons-o1U13Vcg.js.map} +1 -1
  32. package/dist/{design-builder-CqgTpp9B.js → design-builder-CoCtjIwj.js} +86 -86
  33. package/dist/{design-builder-CqgTpp9B.js.map → design-builder-CoCtjIwj.js.map} +1 -1
  34. package/dist/{embed-CLz3CTcX.js → embed-SOFUZlGB.js} +843 -729
  35. package/dist/embed-SOFUZlGB.js.map +1 -0
  36. package/dist/embed.mjs +28 -28
  37. package/dist/errors/editorError.d.ts +7 -2
  38. package/dist/{guards-DldCuuJq.js → guards-DMmhw_Ow.js} +4 -4
  39. package/dist/{guards-DldCuuJq.js.map → guards-DMmhw_Ow.js.map} +1 -1
  40. package/dist/index.mjs +21 -21
  41. package/dist/internals.mjs +108 -108
  42. package/dist/internals.mjs.map +1 -1
  43. package/dist/placement-Br5jI_-u.js +90 -0
  44. package/dist/placement-Br5jI_-u.js.map +1 -0
  45. package/dist/placement.d.ts +2 -0
  46. package/dist/placement.mjs +5 -0
  47. package/dist/placement.mjs.map +1 -0
  48. package/dist/template-fields-CBLHgW7G.js +556 -0
  49. package/dist/template-fields-CBLHgW7G.js.map +1 -0
  50. package/dist/testing.mjs +24 -24
  51. package/dist/{useCommands-CQi4Pyd2.js → useCommands-x8J7_Mzy.js} +262 -262
  52. package/dist/{useCommands-CQi4Pyd2.js.map → useCommands-x8J7_Mzy.js.map} +1 -1
  53. package/dist/usePieceGuideBundle-C7r06a_x.js +65 -0
  54. package/dist/usePieceGuideBundle-C7r06a_x.js.map +1 -0
  55. package/dist/{validation-THw5W6N_.js → validation-BG-V98h0.js} +846 -846
  56. package/dist/validation-BG-V98h0.js.map +1 -0
  57. package/example/package.json +1 -1
  58. package/package.json +9 -4
  59. package/dist/EditorCanvasImpl-BRdFWZA5.js +0 -81
  60. package/dist/EditorCanvasImpl-BRdFWZA5.js.map +0 -1
  61. package/dist/FontAnalyzer-CEYA_6fY.js.map +0 -1
  62. package/dist/SnowconeCanvas-BaFR8QXs.js +0 -1196
  63. package/dist/SnowconeCanvas-BaFR8QXs.js.map +0 -1
  64. package/dist/embed-CLz3CTcX.js.map +0 -1
  65. package/dist/template-fields-D8uOutD4.js +0 -505
  66. package/dist/template-fields-D8uOutD4.js.map +0 -1
  67. package/dist/usePieceGuideBundle-BUQKb0Ja.js +0 -61
  68. package/dist/usePieceGuideBundle-BUQKb0Ja.js.map +0 -1
  69. package/dist/validation-THw5W6N_.js.map +0 -1
package/ERROR_CONTRACT.md CHANGED
@@ -36,7 +36,8 @@ the issue key in `error.data.issue`.
36
36
  | [`E_EMBED_CONFIG`](#e_embed_config) | throw | Embed wiring invalid at mount (missing grant/mockupIds, variant or placement mismatch) — the throw-mode upgrade of `validateEmbed` (F3). |
37
37
  | [`E_PRODUCT_NOT_RENDERABLE`](#e_product_not_renderable) | throw | `productToRenderConfig`: the catalog product has no renderable image placement / no mockup scenes, or the requested placement doesn't exist. |
38
38
  | [`E_DUPLICATE_ARTBOARD`](#e_duplicate_artboard) | throw | `design().artboard()`: an artboard with this label already exists in the design (artboard names are placement labels — unique by contract). |
39
- | [`E_UNKNOWN_PLACEMENT`](#e_unknown_placement) | throw | `usePlacements().select()`: the label names no artboard in the design (ADR-0088). Lists the valid placement labels. |
39
+ | [`E_UNKNOWN_PLACEMENT`](#e_unknown_placement) | throw | `usePlacements().select()` / `.split()`: the label is neither a required product placement nor a design artboard (ADR-0088 / ADR-0101). Lists the valid placement labels. |
40
+ | [`E_LINK_BASE`](#e_link_base) | throw | `usePlacements().link()`: the label is the BASE artboard — the one every linked placement inherits — so it cannot be re-linked (ADR-0101). Link a split placement instead, or call `linkAll()`. |
40
41
  | [`E_UNKNOWN_OPTION`](#e_unknown_option) | throw | `select()` named an option key/choice that doesn't exist on the product (Buy-owned selection, ADR-0085 §2 / P2). |
41
42
  | [`E_COLOR_ROLE_COLLISION`](#e_color_role_collision) | throw | `design({ colors })`: two color roles share one (normalized) hex value. `useColorBinding` matches by VALUE, so colliding roles would capture each other's elements — rejected at mint time instead of failing subtly. |
42
43
  | [`E_COLOR_ROLE_INVALID`](#e_color_role_invalid) | throw | `design({ colors })`: a role's value is not a `#RGB`/`#RRGGBB` hex (value-matched bindings normalize hex; other syntaxes can't bind). |
@@ -62,7 +63,7 @@ the issue key in `error.data.issue`.
62
63
  | [`missing-grant`](#missing-grant) | error → throws `E_EMBED_CONFIG` | No grant source (`grantUrl`/`grant`) — the session can never authorize, so no mockup ever renders. |
63
64
  | [`empty-mockup-ids`](#empty-mockup-ids) | error → throws `E_EMBED_CONFIG` | `product.mockupIds` is empty — nothing to render. |
64
65
  | [`variant-required`](#variant-required) | error → throws `E_EMBED_CONFIG` | The product has a variant axis but no `variantId` — the render would hang ~30s waiting on a color blob that never comes. |
65
- | [`multi-placement`](#multi-placement) | error → throws `E_EMBED_CONFIG` | The product requires several placements but this embed renders ONE — the server times out waiting on the rest (needs the eject path). |
66
+ | [`placements-inherit-base`](#placements-inherit-base) | warn (advisory) | ADR-0101 advisory: the design authors 2+ artboards but leaves some required placements without one — those placements INHERIT the base (the first artboard), re-fit per placement. Not an error: base + overrides is the model. Assert on this to catch a half-finished per-placement seed. |
66
67
  | [`placement-mismatch`](#placement-mismatch) | error → throws `E_EMBED_CONFIG` | The design's placement isn't one of the product's placements — the server renders a blank mockup. |
67
68
  | [`duplicate-layer-names`](#duplicate-layer-names) | warn (advisory) | Two layers share a name — name-bound hooks bind the FIRST match, so a control may edit the wrong layer. |
68
69
  | [`bundle-role-required`](#bundle-role-required) | error → throws `E_EMBED_CONFIG` | BUNDLE session (ADR-0099): a design artboard is a bare label (`"Front"`) instead of a role-scoped key (`"top.front"`), or a bare `variantId` is set — the server refuses both (`E_BUNDLE_ROLE_REQUIRED`): two members can both have a "Front". |
@@ -361,18 +362,48 @@ the issue key in `error.data.issue`.
361
362
 
362
363
  ### `E_UNKNOWN_PLACEMENT`
363
364
 
364
- **Fires when:** `usePlacements().select()`: the label names no artboard in the design (ADR-0088). Lists the valid placement labels.
365
+ **Fires when:** `usePlacements().select()` / `.split()`: the label is neither a required product placement nor a design artboard (ADR-0088 / ADR-0101). Lists the valid placement labels.
365
366
 
366
367
  **Docs:** <https://developers.snowcone.app/embed-mode> (`error.data.docSlug`)
367
368
 
368
369
  - `src/components/editor/EditorRoot.tsx`
369
370
 
370
371
  ```text
371
- no placement "${label}" in this design — its artboards are ${JSON.stringify(placements)}. Add it to the seed with design(…).artboard('${label}', { width, height }) (ADR-0088).
372
+ no placement "${label}" — the product's placements are ${JSON.stringify(requiredPlacements)} and the design's artboards are ${JSON.stringify(placements)} (ADR-0088 / ADR-0101).
372
373
  ```
373
374
 
374
375
  `error.data`: `code`, `placement`, `validNames`, `docSlug`
375
376
 
377
+ - `src/components/editor/EditorRoot.tsx`
378
+
379
+ ```text
380
+ cannot split "${label}" — the product's placements are ${JSON.stringify(requiredPlacements)} (ADR-0101).
381
+ ```
382
+
383
+ `error.data`: `code`, `placement`, `validNames`, `docSlug`
384
+
385
+ ### `E_LINK_BASE`
386
+
387
+ **Fires when:** `usePlacements().link()`: the label is the BASE artboard — the one every linked placement inherits — so it cannot be re-linked (ADR-0101). Link a split placement instead, or call `linkAll()`.
388
+
389
+ **Docs:** <https://developers.snowcone.app/embed-mode> (`error.data.docSlug`)
390
+
391
+ - `src/components/editor/EditorRoot.tsx`
392
+
393
+ ```text
394
+ "${label}" is the base artboard — every linked placement inherits it, so it cannot be re-linked. Link a split placement instead, or call linkAll(). Artboards: ${JSON.stringify(placements)}.
395
+ ```
396
+
397
+ `error.data`: `code`, `artboardName`, `validNames`, `docSlug`
398
+
399
+ - `src/components/editor/designStateStore.ts`
400
+
401
+ ```text
402
+ "${label}" is the base artboard — every linked placement inherits it, so it cannot be re-linked. Link a split placement instead, or call linkAll(). Artboards: ${JSON.stringify(this.artboardNames())}.
403
+ ```
404
+
405
+ `error.data`: `code`, `artboardName`, `validNames`, `docSlug`
406
+
376
407
  ### `E_UNKNOWN_OPTION`
377
408
 
378
409
  **Fires when:** `select()` named an option key/choice that doesn't exist on the product (Buy-owned selection, ADR-0085 §2 / P2).
@@ -737,16 +768,16 @@ so `Editor.Root` throws them as `E_EMBED_CONFIG` (the issue key is in
737
768
  ${err instanceof Error ? err.message : String(err)}
738
769
  ```
739
770
 
740
- ### `multi-placement`
771
+ ### `placements-inherit-base`
741
772
 
742
- **Severity:** error (throws as `E_EMBED_CONFIG`)
773
+ **Severity:** warn (advisory)
743
774
 
744
- **Fires when:** The product requires several placements but this embed renders ONE — the server times out waiting on the rest (needs the eject path).
775
+ **Fires when:** ADR-0101 advisory: the design authors 2+ artboards but leaves some required placements without one — those placements INHERIT the base (the first artboard), re-fit per placement. Not an error: base + overrides is the model. Assert on this to catch a half-finished per-placement seed.
745
776
 
746
777
  - `src/components/embed-realtime/validateEmbed.ts`
747
778
 
748
779
  ```text
749
- product ${product.productId} requires placements [${required.join(', ')}] but the design only has artboards [${artboardNames.join(', ')}] — missing [${missing.join(', ')}]. The render would time out waiting for the missing canvas_states. Add each missing placement to the seed: design(…).artboard('${missing[0]}', { width, height })… (ADR-0088), or use the eject path — see https://developers.snowcone.app/embed-mode#eject.
780
+ product ${product.productId} requires placements [${required.join(', ')}]; the design authors [${artboardNames.join(', ')}], so [${inheriting.join(', ')}] inherit the base artboard "${artboardNames[0]}" (re-fit per placement, ADR-0101). Intended? Otherwise split them: design(…).artboard('${inheriting[0]}', { width, height })… or usePlacements().split('${inheriting[0]}') at runtime.
750
781
  ```
751
782
 
752
783
  ### `placement-mismatch`
@@ -29,6 +29,7 @@ stable hooks; `sc:`-prefixed utility classes are internal and NOT contract.
29
29
  | `menu` | Editor.Menu |
30
30
  | `placements` | Editor.Placements |
31
31
  | `placements-item` | Editor.Placements |
32
+ | `placements-link-toggle` | Editor.Placements |
32
33
  | `preview` | Editor.Preview |
33
34
  | `preview-error` | Editor.Preview |
34
35
  | `preview-image` | Editor.Preview |
@@ -200,20 +201,41 @@ stable hooks; `sc:`-prefixed utility classes are internal and NOT contract.
200
201
  <input type="file">
201
202
  ```
202
203
 
203
- ### `placements` — Editor.Placements — two-placement design
204
+ ### `placements` — Editor.Placements — split (per-placement artboards): tabs + re-link action
204
205
 
205
206
  ```html
206
207
  <div data-sc-part="placements">
207
208
  <button aria-pressed="true" data-sc-active="true" data-sc-part="placements-item" type="button">
208
209
  <button aria-pressed="false" data-sc-active="false" data-sc-part="placements-item" type="button">
210
+ <button data-sc-part="placements-link-toggle" type="button">
209
211
  ```
210
212
 
211
- ### `placements-item` — Editor.Placements — one tab (active state in data-sc-active)
213
+ ### `placements` — Editor.Placements — linked (one design on every area, the default)
214
+
215
+ ```html
216
+ <div data-sc-part="placements">
217
+ <span data-sc-active="true" data-sc-part="placements-item" title=<title>>
218
+ <button data-sc-part="placements-link-toggle" type="button">
219
+ ```
220
+
221
+ ### `placements-item` — Editor.Placements — one tab (active in data-sc-active, inherited in data-sc-linked)
212
222
 
213
223
  ```html
214
224
  <button aria-pressed="true" data-sc-active="true" data-sc-part="placements-item" type="button">
215
225
  ```
216
226
 
227
+ ### `placements-link-toggle` — Editor.Placements — "Use one design for all" (split state)
228
+
229
+ ```html
230
+ <button data-sc-part="placements-link-toggle" type="button">
231
+ ```
232
+
233
+ ### `placements-link-toggle` — Editor.Placements — "Customize separately" (linked state)
234
+
235
+ ```html
236
+ <button data-sc-part="placements-link-toggle" type="button">
237
+ ```
238
+
217
239
  ### `preview` — Editor.Preview — awaiting the first mockup
218
240
 
219
241
  ```html
@@ -1,6 +1,6 @@
1
- import { a5 as ie, a6 as ae, aG as nt, a7 as Ot, aT as re, aR as le, aK as Yt, aQ as he, aJ as gt, aU as ce, aV as Gt, aW as de, aX as fe, c as Jt, aY as ge, aZ as ue, a_ as pe, a$ as ye, ae as Qt, aa as te, ai as pt, ab as ee, ac as Lt, aj as Xt, ah as Pt, ak as Nt, af as be, al as Se, b0 as Kt, b1 as me, b2 as Te, b3 as we, I as ve, b4 as xe, b5 as Me, b6 as tt } from "./FontAnalyzer-CEYA_6fY.js";
2
- import { T as Wt, b as Re, y as Ce, O as ke, P as Dt, F as At, Q as Pe, R as Ae, S as Ee, U as $t } from "./ExportManager-CDX5_up8.js";
3
- import { i as _t, a as He } from "./guards-DldCuuJq.js";
1
+ import { k as ie, l as ae, Y as nt, m as Ot, Z as re, _ as le, $ as Yt, a0 as he, a1 as gt, a2 as ce, a3 as Gt, a4 as de, a5 as fe, c as Jt, a6 as ge, a7 as ue, a8 as pe, a9 as ye, aa as Qt, s as te, ab as pt, w as ee, ac as Lt, ad as Xt, ae as Pt, af as Nt, ag as be, ah as Se, ai as Kt, aj as me, ak as Te, al as we, I as ve, am as xe, an as Me, ao as tt } from "./FontAnalyzer-DWHS41ba.js";
2
+ import { T as Wt, b as Re, u as Ce, K as ke, L as Dt, z as At, M as Pe, N as Ae, O as Ee, P as $t } from "./ExportManager-BbBfO22S.js";
3
+ import { a as _t, i as He } from "./guards-DMmhw_Ow.js";
4
4
  const ct = {
5
5
  // Canvas-specific
6
6
  canvas: {
@@ -3456,4 +3456,4 @@ export {
3456
3456
  jt as g,
3457
3457
  Je as r
3458
3458
  };
3459
- //# sourceMappingURL=CanvasRenderer-CTN4j-uO.js.map
3459
+ //# sourceMappingURL=CanvasRenderer-D-E5WWLm.js.map