@snowcone-app/ui 0.4.7 → 0.4.9
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/CHANGELOG.md +18 -0
- package/dist/index.cjs +305 -272
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +78 -45
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/patterns/RealtimeProvider.tsx +86 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowcone-app/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "React components for merchandise visualization and customization",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"react-instantsearch": "^7.15.5",
|
|
104
104
|
"react-zoom-pan-pinch": "^3.6.4",
|
|
105
105
|
"tailwind-merge": "^3.0.0",
|
|
106
|
-
"@snowcone-app/sdk": "0.20.
|
|
106
|
+
"@snowcone-app/sdk": "0.20.2"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@chromatic-com/storybook": "^4.1.2",
|
|
@@ -42,6 +42,7 @@ import type { RealtimeRenderError } from "@snowcone-app/sdk";
|
|
|
42
42
|
import { readEnv } from "../lib/env";
|
|
43
43
|
import { useProductOptional } from "./Product";
|
|
44
44
|
import type { ReactProductContext } from "./Product";
|
|
45
|
+
import { useMockupBackgroundOptional } from "./MockupBackground";
|
|
45
46
|
|
|
46
47
|
// Default artboard size for export calculations (matches @snowcone-app/canvas default)
|
|
47
48
|
const DEFAULT_ARTBOARD_SIZE = 2000;
|
|
@@ -126,6 +127,26 @@ export interface RealtimeContextValue {
|
|
|
126
127
|
*/
|
|
127
128
|
updatePlacementSettings: (settings: Record<string, { scaleMode?: 'fill' | 'fit'; tiles?: number; alignment?: string }>) => boolean;
|
|
128
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Declare spread-mode crop geometry (ADR-0060) for canvases that run
|
|
132
|
+
* combined spread/group artboards: artboard name → artboard dims + piece
|
|
133
|
+
* rects keyed by the product's placement labels. Sent on the realtime
|
|
134
|
+
* config (re-sent when it changes) so the server can slice each rendered
|
|
135
|
+
* spread canvas_state into per-placement images. Pass undefined to clear.
|
|
136
|
+
*/
|
|
137
|
+
setSpreadCrops: (
|
|
138
|
+
crops:
|
|
139
|
+
| Record<
|
|
140
|
+
string,
|
|
141
|
+
{
|
|
142
|
+
width: number;
|
|
143
|
+
height: number;
|
|
144
|
+
pieces: Array<{ placement: string; x: number; y: number; width: number; height: number }>;
|
|
145
|
+
}
|
|
146
|
+
>
|
|
147
|
+
| undefined
|
|
148
|
+
) => void;
|
|
149
|
+
|
|
129
150
|
// PERFORMANCE: Direct access without state updates
|
|
130
151
|
getMockupResultsImmediate: () => MockupResult[];
|
|
131
152
|
|
|
@@ -273,6 +294,9 @@ export function RealtimeProvider({
|
|
|
273
294
|
grantUrl,
|
|
274
295
|
}: RealtimeProviderProps) {
|
|
275
296
|
const productContext = useProductOptional();
|
|
297
|
+
// Page background for renders (see MockupBackground) — sent on the config so
|
|
298
|
+
// realtime results match static `bg=` mockup URLs.
|
|
299
|
+
const mockupBg = useMockupBackgroundOptional();
|
|
276
300
|
|
|
277
301
|
const [isEnabled, setIsEnabled] = useState(autoEnable);
|
|
278
302
|
|
|
@@ -305,6 +329,22 @@ export function RealtimeProvider({
|
|
|
305
329
|
|
|
306
330
|
// Track last sent variantId to detect when we need to re-send config
|
|
307
331
|
const lastSentVariantIdRef = useRef<string | null>(null);
|
|
332
|
+
const lastSentBgRef = useRef<string | undefined>(undefined);
|
|
333
|
+
// Spread crop geometry (ADR-0060) declared via setSpreadCrops. Ref holds
|
|
334
|
+
// the payload; the version state re-runs the config effect on change.
|
|
335
|
+
const spreadCropsRef = useRef<
|
|
336
|
+
| Record<
|
|
337
|
+
string,
|
|
338
|
+
{
|
|
339
|
+
width: number;
|
|
340
|
+
height: number;
|
|
341
|
+
pieces: Array<{ placement: string; x: number; y: number; width: number; height: number }>;
|
|
342
|
+
}
|
|
343
|
+
>
|
|
344
|
+
| undefined
|
|
345
|
+
>(undefined);
|
|
346
|
+
const [spreadCropsVersion, setSpreadCropsVersion] = useState(0);
|
|
347
|
+
const lastSentSpreadCropsRef = useRef<string>("null");
|
|
308
348
|
|
|
309
349
|
// Track last sent colors to prevent duplicates
|
|
310
350
|
const lastSentColorsRef = useRef<Record<string, string>>({});
|
|
@@ -577,6 +617,8 @@ export function RealtimeProvider({
|
|
|
577
617
|
disconnect();
|
|
578
618
|
configSentRef.current = false;
|
|
579
619
|
lastSentVariantIdRef.current = null;
|
|
620
|
+
lastSentBgRef.current = undefined;
|
|
621
|
+
lastSentSpreadCropsRef.current = "null";
|
|
580
622
|
lastSentColorsRef.current = {};
|
|
581
623
|
};
|
|
582
624
|
}
|
|
@@ -611,8 +653,14 @@ export function RealtimeProvider({
|
|
|
611
653
|
const isFirstSend = !configSentRef.current;
|
|
612
654
|
const variantChanged = lastSentVariantIdRef.current !== null &&
|
|
613
655
|
lastSentVariantIdRef.current !== variantId;
|
|
614
|
-
|
|
615
|
-
|
|
656
|
+
// 3. Background changed (theme flip swaps the MockupBackground hex)
|
|
657
|
+
const bgChanged = configSentRef.current && lastSentBgRef.current !== mockupBg;
|
|
658
|
+
// 4. Spread crop geometry changed (spread-mode editor mounted/unmounted)
|
|
659
|
+
const spreadCropsJson = JSON.stringify(spreadCropsRef.current ?? null);
|
|
660
|
+
const spreadCropsChanged =
|
|
661
|
+
configSentRef.current && lastSentSpreadCropsRef.current !== spreadCropsJson;
|
|
662
|
+
|
|
663
|
+
if (!isFirstSend && !variantChanged && !bgChanged && !spreadCropsChanged) {
|
|
616
664
|
return; // Nothing changed, skip
|
|
617
665
|
}
|
|
618
666
|
|
|
@@ -637,6 +685,8 @@ export function RealtimeProvider({
|
|
|
637
685
|
variantId,
|
|
638
686
|
shop: shop as string,
|
|
639
687
|
width: mockupWidth,
|
|
688
|
+
...(mockupBg ? { bg: mockupBg } : {}),
|
|
689
|
+
...(spreadCropsRef.current ? { spreadCrops: spreadCropsRef.current } : {}),
|
|
640
690
|
placementSettings: { ...placementSettingsOverridesRef.current },
|
|
641
691
|
}
|
|
642
692
|
: {
|
|
@@ -646,6 +696,8 @@ export function RealtimeProvider({
|
|
|
646
696
|
shop: "hTidnpNnNa",
|
|
647
697
|
seal: "bypass-sig-for-k6-load-test",
|
|
648
698
|
width: mockupWidth,
|
|
699
|
+
...(mockupBg ? { bg: mockupBg } : {}),
|
|
700
|
+
...(spreadCropsRef.current ? { spreadCrops: spreadCropsRef.current } : {}),
|
|
649
701
|
placementSettings: { ...placementSettingsOverridesRef.current },
|
|
650
702
|
};
|
|
651
703
|
|
|
@@ -662,6 +714,8 @@ export function RealtimeProvider({
|
|
|
662
714
|
|
|
663
715
|
configSentRef.current = true;
|
|
664
716
|
lastSentVariantIdRef.current = variantId;
|
|
717
|
+
lastSentBgRef.current = mockupBg;
|
|
718
|
+
lastSentSpreadCropsRef.current = spreadCropsJson;
|
|
665
719
|
|
|
666
720
|
// Send default colors for color placements
|
|
667
721
|
setTimeout(() => {
|
|
@@ -682,6 +736,8 @@ export function RealtimeProvider({
|
|
|
682
736
|
sendConfigRaw,
|
|
683
737
|
sendColorBlobTracked,
|
|
684
738
|
mockupWidth,
|
|
739
|
+
mockupBg,
|
|
740
|
+
spreadCropsVersion,
|
|
685
741
|
]);
|
|
686
742
|
|
|
687
743
|
// Watch for color placement changes
|
|
@@ -725,6 +781,32 @@ export function RealtimeProvider({
|
|
|
725
781
|
[updatePlacementSettingsRaw]
|
|
726
782
|
);
|
|
727
783
|
|
|
784
|
+
// Declare spread crop geometry (ADR-0060). Stored in a ref; bumping the
|
|
785
|
+
// version state re-runs the config effect, which re-sends the config when
|
|
786
|
+
// the serialized geometry actually changed.
|
|
787
|
+
const setSpreadCrops = useCallback(
|
|
788
|
+
(
|
|
789
|
+
crops:
|
|
790
|
+
| Record<
|
|
791
|
+
string,
|
|
792
|
+
{
|
|
793
|
+
width: number;
|
|
794
|
+
height: number;
|
|
795
|
+
pieces: Array<{ placement: string; x: number; y: number; width: number; height: number }>;
|
|
796
|
+
}
|
|
797
|
+
>
|
|
798
|
+
| undefined
|
|
799
|
+
) => {
|
|
800
|
+
const next = crops && Object.keys(crops).length > 0 ? crops : undefined;
|
|
801
|
+
if (JSON.stringify(next ?? null) === JSON.stringify(spreadCropsRef.current ?? null)) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
spreadCropsRef.current = next;
|
|
805
|
+
setSpreadCropsVersion((v) => v + 1);
|
|
806
|
+
},
|
|
807
|
+
[]
|
|
808
|
+
);
|
|
809
|
+
|
|
728
810
|
const enableRealtime = useCallback(() => {
|
|
729
811
|
setIsEnabled(true);
|
|
730
812
|
}, []);
|
|
@@ -1099,6 +1181,7 @@ export function RealtimeProvider({
|
|
|
1099
1181
|
sendCanvasState,
|
|
1100
1182
|
updateMockupIds: updateMockupIdsRaw,
|
|
1101
1183
|
updatePlacementSettings: updatePlacementSettingsWrapped,
|
|
1184
|
+
setSpreadCrops,
|
|
1102
1185
|
getMockupResultsImmediate: () => mockupResultsRef.current,
|
|
1103
1186
|
subscribeMockupResults: stableSubscribeMockupResults,
|
|
1104
1187
|
subscribeMockupResultById: stableSubscribeMockupResultById,
|
|
@@ -1135,6 +1218,7 @@ export function RealtimeProvider({
|
|
|
1135
1218
|
sendCanvasState,
|
|
1136
1219
|
updateMockupIdsRaw,
|
|
1137
1220
|
updatePlacementSettingsWrapped,
|
|
1221
|
+
setSpreadCrops,
|
|
1138
1222
|
stableSubscribeMockupResults,
|
|
1139
1223
|
stableSubscribeMockupResultById,
|
|
1140
1224
|
notifyPendingExport,
|