@snowcone-app/canvas 0.36.4 → 0.37.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.
- package/ERROR_CONTRACT.md +137 -0
- package/dist/{ContextualToolbars-GAkUFq2m.js → ContextualToolbars-CG5Gmv_i.js} +3 -3
- package/dist/{ContextualToolbars-GAkUFq2m.js.map → ContextualToolbars-CG5Gmv_i.js.map} +1 -1
- package/dist/{EditorCanvasImpl-B43TaPgW.js → EditorCanvasImpl-BRdFWZA5.js} +5 -5
- package/dist/{EditorCanvasImpl-B43TaPgW.js.map → EditorCanvasImpl-BRdFWZA5.js.map} +1 -1
- package/dist/FontAnalyzer-CEYA_6fY.js.map +1 -1
- package/dist/{ImagePanel-DK3Zflyc.js → ImagePanel-DLxIDKV-.js} +131 -131
- package/dist/ImagePanel-DLxIDKV-.js.map +1 -0
- package/dist/{SnowconeCanvas-B1lq53my.js → SnowconeCanvas-BaFR8QXs.js} +6 -6
- package/dist/{SnowconeCanvas-B1lq53my.js.map → SnowconeCanvas-BaFR8QXs.js.map} +1 -1
- package/dist/advanced.mjs +7 -7
- package/dist/api/embed.d.ts +3 -0
- package/dist/components/embed-realtime/validateEmbed.d.ts +13 -0
- package/dist/{custom-icons-wZuopKx7.js → custom-icons-D51KIAfz.js} +2 -2
- package/dist/{custom-icons-wZuopKx7.js.map → custom-icons-D51KIAfz.js.map} +1 -1
- package/dist/design/bundle-config.d.ts +127 -0
- package/dist/design/product-config.d.ts +3 -0
- package/dist/embed-CLz3CTcX.js +1799 -0
- package/dist/embed-CLz3CTcX.js.map +1 -0
- package/dist/embed.mjs +50 -45
- package/dist/errors/editorError.d.ts +28 -1
- package/dist/index.mjs +5 -5
- package/dist/{template-fields-CMK7aqCw.js → template-fields-D8uOutD4.js} +2 -2
- package/dist/{template-fields-CMK7aqCw.js.map → template-fields-D8uOutD4.js.map} +1 -1
- package/dist/{useCommands-DC6ZPY1n.js → useCommands-CQi4Pyd2.js} +2 -2
- package/dist/{useCommands-DC6ZPY1n.js.map → useCommands-CQi4Pyd2.js.map} +1 -1
- package/dist/{validation-Bm2noYgt.js → validation-THw5W6N_.js} +7 -7
- package/dist/{validation-Bm2noYgt.js.map → validation-THw5W6N_.js.map} +1 -1
- package/example/package.json +1 -1
- package/package.json +4 -4
- package/dist/ImagePanel-DK3Zflyc.js.map +0 -1
- package/dist/embed-DbCQZ8Q9.js +0 -1588
- package/dist/embed-DbCQZ8Q9.js.map +0 -1
package/ERROR_CONTRACT.md
CHANGED
|
@@ -52,6 +52,12 @@ the issue key in `error.data.issue`.
|
|
|
52
52
|
| [`E_AUDIT_NO_ROOT`](#e_audit_no_root) | throw | `auditEditor()` called with no `<Editor.Root>` mounted — the imperative audit reads the most-recent mounted Root's registry (#10). |
|
|
53
53
|
| [`E_FONT_LOAD_FAILED`](#e_font_load_failed) | throw | A custom (brand) font failed to load from its hosted URL — the binary fetch failed (CORS, 404, network) or FontFace rejected the bytes. Custom fonts NEVER fall back silently to a system face: the design would render with a different typeface/metrics everywhere it's consumed. `data` carries `family`, `url`, `weight`, `style`. Fix the URL (must serve the font file over a plain cross-origin GET) and re-register. Dev AND prod. |
|
|
54
54
|
| [`E_DISTRIBUTE_MIN_THREE`](#e_distribute_min_three) | throw | `computeDistribute()` called with fewer than 3 movable elements — distributing needs something between the two ends. Gate the action on `useAlignDistribute().canDistribute` instead of calling below the threshold (the toolbar hides its distribute buttons for exactly this reason). |
|
|
55
|
+
| [`E_BUNDLE_CONFIG_REQUIRED`](#e_bundle_config_required) | throw | `productToRenderConfig()` was handed a BUNDLE (a catalog product with a `bundle` block — several products in one mockup, ADR-0099). A bundle has no single `variantId` and its placements are role-scoped (`top.front`), so the single-product adapter cannot describe it: use `bundleToRenderConfig()` instead. Never renders the wrong thing. |
|
|
56
|
+
| [`E_NOT_A_BUNDLE`](#e_not_a_bundle) | throw | `bundleToRenderConfig()` was handed a product with no `bundle` block — `picks` / `variants` are bundle parameters the server refuses on a single product. Use `productToRenderConfig()`. |
|
|
57
|
+
| [`E_BUNDLE_ROLE_REQUIRED`](#e_bundle_role_required) | throw | A bundle placement was named WITHOUT its role (`"front"` instead of `"top.front"`): `options.placement` on `bundleToRenderConfig()`, or a design artboard on a bundle session. Two members can both have a "Front", so the bare label is never the identity — `validNames` lists the role-scoped keys. |
|
|
58
|
+
| [`E_BUNDLE_ROLE_NOT_FOUND`](#e_bundle_role_not_found) | throw | `picks` / `variants` named a role the bundle has no slot for. `validNames` lists the bundle's roles. |
|
|
59
|
+
| [`E_BUNDLE_PICK_NOT_FOUND`](#e_bundle_pick_not_found) | throw | `picks[role]` named a product that is not one of that slot's alternatives. `validNames` lists the slot's alternatives. |
|
|
60
|
+
| [`E_BUNDLE_VARIANT_NOT_FOUND`](#e_bundle_variant_not_found) | throw | A slot has no resolvable variant: `variants[role]` is not one of the picked alternative's variant gvids (`validNames`), or the alternative publishes no variant at all. Every role of a bundle session needs a variant — the server keys the render on the per-role set. |
|
|
55
61
|
| [`missing-shop`](#missing-shop) | error → throws `E_EMBED_CONFIG` | No `shop` (the publishable shop id) was passed. |
|
|
56
62
|
| [`missing-grant`](#missing-grant) | error → throws `E_EMBED_CONFIG` | No grant source (`grantUrl`/`grant`) — the session can never authorize, so no mockup ever renders. |
|
|
57
63
|
| [`empty-mockup-ids`](#empty-mockup-ids) | error → throws `E_EMBED_CONFIG` | `product.mockupIds` is empty — nothing to render. |
|
|
@@ -59,6 +65,8 @@ the issue key in `error.data.issue`.
|
|
|
59
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). |
|
|
60
66
|
| [`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. |
|
|
61
67
|
| [`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
|
+
| [`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". |
|
|
69
|
+
| [`bundle-variant-required`](#bundle-variant-required) | error → throws `E_EMBED_CONFIG` | BUNDLE session (ADR-0099): a role has no entry in `product.variants` — the server keys the render on the per-role variant set, so every role needs one (`bundleToRenderConfig` fills defaults; hand-written configs must too). |
|
|
62
70
|
| [`slow-render`](#slow-render) | warn (advisory) | Runtime (not mount-time): a render has been in flight past `SLOW_RENDER_HINT_MS` with no mockup and no error — the silent-hang class. The render may still arrive; this is a heads-up. |
|
|
63
71
|
|
|
64
72
|
## `EditorError` codes
|
|
@@ -289,6 +297,30 @@ the issue key in `error.data.issue`.
|
|
|
289
297
|
|
|
290
298
|
**Docs:** <https://developers.snowcone.app/embed-mode> (`error.data.docSlug`)
|
|
291
299
|
|
|
300
|
+
- `src/design/bundle-config.ts`
|
|
301
|
+
|
|
302
|
+
```text
|
|
303
|
+
bundle ${catalogProduct.id} has no renderable placement "${options.placement}" for picks ${JSON.stringify(picks)}. Keys: [${placementKeys.join(', ')}].
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`error.data`: `code`, `productId`, `requested`, `picks`, `validNames`, `docSlug`
|
|
307
|
+
|
|
308
|
+
- `src/design/bundle-config.ts`
|
|
309
|
+
|
|
310
|
+
```text
|
|
311
|
+
bundle ${catalogProduct.id} has no renderable image placement for picks ${JSON.stringify(picks)} (a role-scoped, non-color placement with width/height). The catalog payload may be incomplete — fetch it via getProduct(), not a search hit.
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`error.data`: `code`, `productId`, `picks`, `docSlug`
|
|
315
|
+
|
|
316
|
+
- `src/design/bundle-config.ts`
|
|
317
|
+
|
|
318
|
+
```text
|
|
319
|
+
bundle ${catalogProduct.id} has no mockup scenes — nothing to render a preview into.
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`error.data`: `code`, `productId`, `docSlug`
|
|
323
|
+
|
|
292
324
|
- `src/design/product-config.ts`
|
|
293
325
|
|
|
294
326
|
```text
|
|
@@ -573,6 +605,81 @@ the issue key in `error.data.issue`.
|
|
|
573
605
|
|
|
574
606
|
`error.data`: `code`, `mode`, `count`, `min`
|
|
575
607
|
|
|
608
|
+
### `E_BUNDLE_CONFIG_REQUIRED`
|
|
609
|
+
|
|
610
|
+
**Fires when:** `productToRenderConfig()` was handed a BUNDLE (a catalog product with a `bundle` block — several products in one mockup, ADR-0099). A bundle has no single `variantId` and its placements are role-scoped (`top.front`), so the single-product adapter cannot describe it: use `bundleToRenderConfig()` instead. Never renders the wrong thing.
|
|
611
|
+
|
|
612
|
+
**Docs:** <https://developers.snowcone.app/bundles> (`error.data.docSlug`)
|
|
613
|
+
|
|
614
|
+
- `src/design/product-config.ts`
|
|
615
|
+
|
|
616
|
+
```text
|
|
617
|
+
product ${catalogProduct.id} is a bundle (roles: [${catalogProduct.bundle.members.map((m) => m.role).join(', ')}]) — it has per-role variants and role-scoped placement keys, which the single-product config cannot describe. Use bundleToRenderConfig(product, { picks?, variants?, placement?, width? }) instead.
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
`error.data`: `code`, `productId`, `validNames`, `docSlug`
|
|
621
|
+
|
|
622
|
+
### `E_NOT_A_BUNDLE`
|
|
623
|
+
|
|
624
|
+
**Fires when:** `bundleToRenderConfig()` was handed a product with no `bundle` block — `picks` / `variants` are bundle parameters the server refuses on a single product. Use `productToRenderConfig()`.
|
|
625
|
+
|
|
626
|
+
- `src/design/bundle-config.ts`
|
|
627
|
+
|
|
628
|
+
```text
|
|
629
|
+
product ${catalogProduct.id} has no bundle block — picks / variants are bundle parameters the server refuses on a single product (E_NOT_A_BUNDLE). Use productToRenderConfig() for it.
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
`error.data`: `code`, `productId`, `docSlug`
|
|
633
|
+
|
|
634
|
+
### `E_BUNDLE_ROLE_REQUIRED`
|
|
635
|
+
|
|
636
|
+
**Fires when:** A bundle placement was named WITHOUT its role (`"front"` instead of `"top.front"`): `options.placement` on `bundleToRenderConfig()`, or a design artboard on a bundle session. Two members can both have a "Front", so the bare label is never the identity — `validNames` lists the role-scoped keys.
|
|
637
|
+
|
|
638
|
+
- `src/design/bundle-config.ts`
|
|
639
|
+
|
|
640
|
+
```text
|
|
641
|
+
placement "${options.placement}" names no role — on bundle ${catalogProduct.id} a placement is addressed by its role-scoped key. Keys: [${placementKeys.join(', ')}].
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
`error.data`: `code`, `productId`, `requested`, `validNames`, `docSlug`
|
|
645
|
+
|
|
646
|
+
### `E_BUNDLE_ROLE_NOT_FOUND`
|
|
647
|
+
|
|
648
|
+
**Fires when:** `picks` / `variants` named a role the bundle has no slot for. `validNames` lists the bundle's roles.
|
|
649
|
+
|
|
650
|
+
- `src/design/bundle-config.ts`
|
|
651
|
+
|
|
652
|
+
```text
|
|
653
|
+
bundle ${catalogProduct.id} has no role "${role}" (in ${field}). Roles: [${roleNames.join(', ')}].
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
`error.data`: `code`, `productId`, `field`, `role`, `validNames`, `docSlug`
|
|
657
|
+
|
|
658
|
+
### `E_BUNDLE_PICK_NOT_FOUND`
|
|
659
|
+
|
|
660
|
+
**Fires when:** `picks[role]` named a product that is not one of that slot's alternatives. `validNames` lists the slot's alternatives.
|
|
661
|
+
|
|
662
|
+
- `src/design/bundle-config.ts`
|
|
663
|
+
|
|
664
|
+
```text
|
|
665
|
+
bundle ${catalogProduct.id} role "${member.role}" has no alternative "${wanted}". Alternatives: [${alternatives.join(', ')}] — pass picks: { ${member.role}: "${alternatives[0]}" }.
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
`error.data`: `code`, `productId`, `role`, `requested`, `validNames`, `docSlug`
|
|
669
|
+
|
|
670
|
+
### `E_BUNDLE_VARIANT_NOT_FOUND`
|
|
671
|
+
|
|
672
|
+
**Fires when:** A slot has no resolvable variant: `variants[role]` is not one of the picked alternative's variant gvids (`validNames`), or the alternative publishes no variant at all. Every role of a bundle session needs a variant — the server keys the render on the per-role set.
|
|
673
|
+
|
|
674
|
+
- `src/design/bundle-config.ts`
|
|
675
|
+
|
|
676
|
+
```text
|
|
677
|
+
${accepted.length === 0 ? `bundle ${catalogProduct.id} role "${member.role}": alternative ${alt.productId} publishes no variant — `}nothing can render this slot (catalog data is incomplete).'
|
|
678
|
+
: `bundle ${catalogProduct.id} role "${member.role}" (${alt.productId}) has no variant "${requested}". Variants: [${accepted.join(', ')}] — pass variants: { ${member.role}: "${accepted[0]}" }.
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
`error.data`: `code`, `productId`, `role`, `memberProductId`, `requested`, `validNames`, `docSlug`
|
|
682
|
+
|
|
576
683
|
## Embed wiring issues (the `onIssue` channel)
|
|
577
684
|
|
|
578
685
|
Mount-time wiring problems and runtime stalls are `EmbedIssue` objects
|
|
@@ -666,6 +773,36 @@ so `Editor.Root` throws them as `E_EMBED_CONFIG` (the issue key is in
|
|
|
666
773
|
duplicate layer name(s) [${[...new Set(dupes)].join(', ')}] — useTextBinding(name) / useImageBinding(name) bind to the FIRST match, so a duplicate name makes a control edit the wrong layer. Keep names unique.
|
|
667
774
|
```
|
|
668
775
|
|
|
776
|
+
### `bundle-role-required`
|
|
777
|
+
|
|
778
|
+
**Severity:** error (throws as `E_EMBED_CONFIG`)
|
|
779
|
+
|
|
780
|
+
**Fires when:** 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".
|
|
781
|
+
|
|
782
|
+
- `src/components/embed-realtime/validateEmbed.ts`
|
|
783
|
+
|
|
784
|
+
```text
|
|
785
|
+
product ${product.productId} is a bundle, but the design's artboard(s) [${roleless.join(', ')}] name no role. On a bundle every placement is addressed by its role-scoped key (<role>.<key>)${keys.length ? ` — keys: [${keys.join(', ')}]` : ''}. Seed the design from bundleToRenderConfig(product).placement.key, not from a bare label.
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
- `src/components/embed-realtime/validateEmbed.ts`
|
|
789
|
+
|
|
790
|
+
```text
|
|
791
|
+
product ${product.productId} is a bundle, but product.variantId ("${product.variantId}") names no slot — the server refuses it (E_BUNDLE_ROLE_REQUIRED). Name the variant per role instead: product.variants = { <role>: <gvid> } (bundleToRenderConfig fills it), and leave variantId unset.
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### `bundle-variant-required`
|
|
795
|
+
|
|
796
|
+
**Severity:** error (throws as `E_EMBED_CONFIG`)
|
|
797
|
+
|
|
798
|
+
**Fires when:** BUNDLE session (ADR-0099): a role has no entry in `product.variants` — the server keys the render on the per-role variant set, so every role needs one (`bundleToRenderConfig` fills defaults; hand-written configs must too).
|
|
799
|
+
|
|
800
|
+
- `src/components/embed-realtime/validateEmbed.ts`
|
|
801
|
+
|
|
802
|
+
```text
|
|
803
|
+
product ${product.productId} is a bundle, but role(s) [${missingVariant.join(', ')}] have no entry in product.variants (${JSON.stringify(product.variants ?? {})}). Every role needs a variant — the server keys the render on the per-role set. Build the product with bundleToRenderConfig(product, { picks, variants }), which fills each slot's default.
|
|
804
|
+
```
|
|
805
|
+
|
|
669
806
|
### `slow-render`
|
|
670
807
|
|
|
671
808
|
**Severity:** warn (advisory)
|
|
@@ -3,8 +3,8 @@ import * as oe from "react";
|
|
|
3
3
|
import Fe, { useRef as V, useEffect as Z, useCallback as S, useState as T, useLayoutEffect as Mo, useMemo as He, memo as Eo, useSyncExternalStore as ur } from "react";
|
|
4
4
|
import { createPortal as Io } from "react-dom";
|
|
5
5
|
import { u as ms, a as Ro, g as fr, h as pr } from "./design-builder-CqgTpp9B.js";
|
|
6
|
-
import { o as zo, I as P, N as Se, Y as hr, O as le, Z as St, B as J, P as mr, T as he, i as me, j as ge, _ as j, n as Ht, d as Lo, U as Ze, W as gr, C as br, f as yr, S as xr } from "./useCommands-
|
|
7
|
-
import { R as gs, L as Me, a as vr, I as Cr, T as st } from "./custom-icons-
|
|
6
|
+
import { o as zo, I as P, N as Se, Y as hr, O as le, Z as St, B as J, P as mr, T as he, i as me, j as ge, _ as j, n as Ht, d as Lo, U as Ze, W as gr, C as br, f as yr, S as xr } from "./useCommands-CQi4Pyd2.js";
|
|
7
|
+
import { R as gs, L as Me, a as vr, I as Cr, T as st } from "./custom-icons-D51KIAfz.js";
|
|
8
8
|
import { M as Ao, ap as Fo, b7 as Oo, H as po, b8 as wr, c as bs, y as kr, b9 as Tr, ba as Nr, bb as Sr, bc as Dr, bd as ho, aA as mo, az as Mr, G as Dt, T as je, g as Er, I as ot, S as $s, P as _o, E as Ir, be as Bo, bf as Rr, L as zr } from "./FontAnalyzer-CEYA_6fY.js";
|
|
9
9
|
import * as Mt from "@radix-ui/react-popover";
|
|
10
10
|
import * as dt from "@radix-ui/react-slider";
|
|
@@ -6529,4 +6529,4 @@ export {
|
|
|
6529
6529
|
Fl as s,
|
|
6530
6530
|
Ei as u
|
|
6531
6531
|
};
|
|
6532
|
-
//# sourceMappingURL=ContextualToolbars-
|
|
6532
|
+
//# sourceMappingURL=ContextualToolbars-CG5Gmv_i.js.map
|