@snowcone-app/ui 0.9.3 → 0.9.4
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 +9 -0
- package/dist/index.cjs +73 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +73 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/composed/HeroProductImage.tsx +14 -3
- package/src/patterns/RealtimeProvider.tsx +98 -37
- package/src/patterns/__tests__/RealtimeProvider.cropDelivery.test.tsx +56 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4344](https://github.com/snowcone-app/snowcone-monorepo/pull/4344) [`379a042`](https://github.com/snowcone-app/snowcone-monorepo/commit/379a042eb69a26f98fe0050f694c4ccf342e55b8) Thanks [@kevinsproles](https://github.com/kevinsproles)! - Per-placement render versions and an explicit re-render. SDK: `MockupResult.versions` (every placement folded into a frame, from boxes that stamp it), `getLatestSentVersions()`, and `rerender(mockupIds)` (answered by `rerender_queued`; refusals are typed `E_RERENDER_*` errors). UI: a picture's `data-render-state` is `current` only when every placement is at its latest sent version, and a picture whose cropped frame failed is re-rendered with `rerender` once the box stamps versions (falling back to forcing it with configs on an `E_RERENDER_*` refusal or an older box).
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`379a042`](https://github.com/snowcone-app/snowcone-monorepo/commit/379a042eb69a26f98fe0050f694c4ccf342e55b8)]:
|
|
10
|
+
- @snowcone-app/sdk@0.25.0
|
|
11
|
+
|
|
3
12
|
## 0.9.3
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -6145,6 +6145,12 @@ function RealtimeProvider({
|
|
|
6145
6145
|
},
|
|
6146
6146
|
onError: (error, detail) => {
|
|
6147
6147
|
console.error(`[RealtimeProvider] Render error [${detail.code}]:`, error);
|
|
6148
|
+
if (detail.code.startsWith("E_RERENDER_") && pendingRerenderRef.current.length > 0) {
|
|
6149
|
+
const ids = pendingRerenderRef.current;
|
|
6150
|
+
pendingRerenderRef.current = [];
|
|
6151
|
+
forceRerenderByConfigRef.current?.(ids);
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
6148
6154
|
if (detail.mockupId) {
|
|
6149
6155
|
const id = detail.mockupId;
|
|
6150
6156
|
setUnavailableMockups((prev) => prev.get(id)?.code === detail.code ? prev : new Map(prev).set(id, detail));
|
|
@@ -6170,7 +6176,9 @@ function RealtimeProvider({
|
|
|
6170
6176
|
disconnect,
|
|
6171
6177
|
flushPendingBlobs,
|
|
6172
6178
|
hasPendingBlobs,
|
|
6173
|
-
getLastSentVersion
|
|
6179
|
+
getLastSentVersion,
|
|
6180
|
+
getLatestSentVersions,
|
|
6181
|
+
rerender: rerenderRaw
|
|
6174
6182
|
} = realtimeMockup;
|
|
6175
6183
|
const mockupResultsRef = (0, import_react25.useRef)([]);
|
|
6176
6184
|
const mockupSubscribersRef = (0, import_react25.useRef)(
|
|
@@ -6208,6 +6216,9 @@ function RealtimeProvider({
|
|
|
6208
6216
|
const compositeBusyRef = (0, import_react25.useRef)(/* @__PURE__ */ new Set());
|
|
6209
6217
|
const compositeCanvasRef = (0, import_react25.useRef)(null);
|
|
6210
6218
|
const requestedMockupIdsRef = (0, import_react25.useRef)([]);
|
|
6219
|
+
const serverStampsVersionsRef = (0, import_react25.useRef)(false);
|
|
6220
|
+
const pendingRerenderRef = (0, import_react25.useRef)([]);
|
|
6221
|
+
const forceRerenderByConfigRef = (0, import_react25.useRef)(null);
|
|
6211
6222
|
const fullFrameRerenderIdsRef = (0, import_react25.useRef)(/* @__PURE__ */ new Set());
|
|
6212
6223
|
const fullFrameRerenderTimerRef = (0, import_react25.useRef)(null);
|
|
6213
6224
|
(0, import_react25.useEffect)(
|
|
@@ -6260,6 +6271,36 @@ function RealtimeProvider({
|
|
|
6260
6271
|
if (!croppedActiveRef.current) return;
|
|
6261
6272
|
sendCropConfig(false);
|
|
6262
6273
|
}, [sendCropConfig]);
|
|
6274
|
+
const sendFullFrameConfig = (0, import_react25.useCallback)(
|
|
6275
|
+
(mockupIds) => {
|
|
6276
|
+
const base = lastConfigBaseRef.current;
|
|
6277
|
+
if (!base) return;
|
|
6278
|
+
sendConfigRaw({
|
|
6279
|
+
...base,
|
|
6280
|
+
mockupIds,
|
|
6281
|
+
placementSettings: { ...placementSettingsOverridesRef.current }
|
|
6282
|
+
});
|
|
6283
|
+
},
|
|
6284
|
+
[sendConfigRaw]
|
|
6285
|
+
);
|
|
6286
|
+
const forceRerenderByConfig = (0, import_react25.useCallback)(
|
|
6287
|
+
(failed) => {
|
|
6288
|
+
const inForce = requestedMockupIdsRef.current;
|
|
6289
|
+
const fp = (list) => [...list].sort().join(",");
|
|
6290
|
+
const step = fp(failed) !== fp(inForce) ? failed : inForce.slice(0, -1);
|
|
6291
|
+
if (step.length === 0) {
|
|
6292
|
+
console.error(
|
|
6293
|
+
`[RealtimeProvider] E_OVERLAY_RERENDER_UNAVAILABLE: cannot force a full-frame re-render of ${failed.join(", ")} (single-mockup session on a box without \`rerender\`); it updates on the next edit.`
|
|
6294
|
+
);
|
|
6295
|
+
sendFullFrameConfig(inForce);
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
6298
|
+
sendFullFrameConfig(step);
|
|
6299
|
+
sendFullFrameConfig(inForce);
|
|
6300
|
+
},
|
|
6301
|
+
[sendFullFrameConfig]
|
|
6302
|
+
);
|
|
6303
|
+
forceRerenderByConfigRef.current = forceRerenderByConfig;
|
|
6263
6304
|
const rerenderFullFrame = (0, import_react25.useCallback)(
|
|
6264
6305
|
(mockupId) => {
|
|
6265
6306
|
fullFrameRerenderIdsRef.current.add(mockupId);
|
|
@@ -6268,35 +6309,26 @@ function RealtimeProvider({
|
|
|
6268
6309
|
fullFrameRerenderTimerRef.current = null;
|
|
6269
6310
|
const failed = [...fullFrameRerenderIdsRef.current];
|
|
6270
6311
|
fullFrameRerenderIdsRef.current.clear();
|
|
6271
|
-
|
|
6272
|
-
if (!base || failed.length === 0) return;
|
|
6312
|
+
if (!lastConfigBaseRef.current || failed.length === 0) return;
|
|
6273
6313
|
const previous = requestedMockupIdsRef.current;
|
|
6274
6314
|
const ids = mergeMockupIds(previous, failed);
|
|
6275
6315
|
croppedActiveRef.current = false;
|
|
6276
6316
|
requestedMockupIdsRef.current = ids;
|
|
6277
|
-
const
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
placementSettings: { ...placementSettingsOverridesRef.current }
|
|
6281
|
-
});
|
|
6282
|
-
const fingerprint = (list) => [...list].sort().join(",");
|
|
6283
|
-
if (fingerprint(ids) !== fingerprint(previous)) {
|
|
6284
|
-
send(ids);
|
|
6317
|
+
const fp = (list) => [...list].sort().join(",");
|
|
6318
|
+
if (fp(ids) !== fp(previous)) {
|
|
6319
|
+
sendFullFrameConfig(ids);
|
|
6285
6320
|
return;
|
|
6286
6321
|
}
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
send(ids);
|
|
6293
|
-
return;
|
|
6322
|
+
if (serverStampsVersionsRef.current) {
|
|
6323
|
+
sendFullFrameConfig(ids);
|
|
6324
|
+
pendingRerenderRef.current = failed;
|
|
6325
|
+
if (rerenderRaw(failed)) return;
|
|
6326
|
+
pendingRerenderRef.current = [];
|
|
6294
6327
|
}
|
|
6295
|
-
|
|
6296
|
-
send(ids);
|
|
6328
|
+
forceRerenderByConfig(failed);
|
|
6297
6329
|
}, 0);
|
|
6298
6330
|
},
|
|
6299
|
-
[
|
|
6331
|
+
[sendFullFrameConfig, forceRerenderByConfig, rerenderRaw]
|
|
6300
6332
|
);
|
|
6301
6333
|
const maybeFlipCrop = (0, import_react25.useCallback)(() => {
|
|
6302
6334
|
if (!croppedDeliveryEnabled || !cropCapableRef.current) return;
|
|
@@ -6331,6 +6363,10 @@ function RealtimeProvider({
|
|
|
6331
6363
|
else merged.push(result);
|
|
6332
6364
|
});
|
|
6333
6365
|
mockupResultsRef.current = merged;
|
|
6366
|
+
if (pendingRerenderRef.current.length > 0) {
|
|
6367
|
+
const delivered = new Set(results.map((r) => r.mockupId));
|
|
6368
|
+
pendingRerenderRef.current = pendingRerenderRef.current.filter((id) => !delivered.has(id));
|
|
6369
|
+
}
|
|
6334
6370
|
const clearedPlacements = /* @__PURE__ */ new Set();
|
|
6335
6371
|
results.forEach((result) => {
|
|
6336
6372
|
const wasPending = pendingMockupIdsRef.current.has(result.mockupId);
|
|
@@ -6452,6 +6488,9 @@ function RealtimeProvider({
|
|
|
6452
6488
|
}
|
|
6453
6489
|
console.log(`[RTP] rawMockupResults changed: ${rawMockupResults.length} result(s)`, rawMockupResults.map((r) => `${r.mockupId}${r.imageUrl ? " \u2713url" : " \u2717NO-URL"}${r.cropRect ? " [crop]" : ""}`));
|
|
6454
6490
|
const displayable = [];
|
|
6491
|
+
if (!serverStampsVersionsRef.current && rawMockupResults.some((r) => r.versions && typeof r.versions === "object")) {
|
|
6492
|
+
serverStampsVersionsRef.current = true;
|
|
6493
|
+
}
|
|
6455
6494
|
rawMockupResults.forEach((result) => {
|
|
6456
6495
|
if (!result.imageUrl) {
|
|
6457
6496
|
console.log(`[RTP] Result for ${result.mockupId} has NO imageUrl \u2014 not caching, pending NOT cleared`);
|
|
@@ -6934,6 +6973,9 @@ function RealtimeProvider({
|
|
|
6934
6973
|
const getLastSentVersionRef = (0, import_react25.useRef)(getLastSentVersion);
|
|
6935
6974
|
getLastSentVersionRef.current = getLastSentVersion;
|
|
6936
6975
|
const getCanvasVersion = (0, import_react25.useCallback)(() => getLastSentVersionRef.current?.() ?? 0, []);
|
|
6976
|
+
const getLatestSentVersionsRef = (0, import_react25.useRef)(getLatestSentVersions);
|
|
6977
|
+
getLatestSentVersionsRef.current = getLatestSentVersions;
|
|
6978
|
+
const getLatestSentVersionsStable = (0, import_react25.useCallback)(() => getLatestSentVersionsRef.current?.() ?? {}, []);
|
|
6937
6979
|
const stableSubscribePipelineSettled = (0, import_react25.useCallback)(
|
|
6938
6980
|
(callback) => {
|
|
6939
6981
|
return subscribePipelineSettledRef.current(callback);
|
|
@@ -6983,6 +7025,7 @@ function RealtimeProvider({
|
|
|
6983
7025
|
getMockupUrl: getMockupUrl3,
|
|
6984
7026
|
getMockupUrls,
|
|
6985
7027
|
getCanvasVersion,
|
|
7028
|
+
getLatestSentVersions: getLatestSentVersionsStable,
|
|
6986
7029
|
isMockupLoading,
|
|
6987
7030
|
registerBlobUrl,
|
|
6988
7031
|
// WebRTC streaming
|
|
@@ -7015,6 +7058,7 @@ function RealtimeProvider({
|
|
|
7015
7058
|
getMockupUrl3,
|
|
7016
7059
|
getMockupUrls,
|
|
7017
7060
|
getCanvasVersion,
|
|
7061
|
+
getLatestSentVersionsStable,
|
|
7018
7062
|
isMockupLoading,
|
|
7019
7063
|
registerBlobUrl
|
|
7020
7064
|
]
|
|
@@ -10769,6 +10813,8 @@ var HeroProductImage = (0, import_react46.memo)(function HeroProductImage2({
|
|
|
10769
10813
|
};
|
|
10770
10814
|
}, [mode, realtimeContext?.subscribePendingBlob, placement]);
|
|
10771
10815
|
const renderVersionRef = (0, import_react46.useRef)(0);
|
|
10816
|
+
const renderVersionsRef = (0, import_react46.useRef)(null);
|
|
10817
|
+
const getLatestSentVersions = realtimeContext?.getLatestSentVersions;
|
|
10772
10818
|
const refusalCodeRef = (0, import_react46.useRef)(null);
|
|
10773
10819
|
refusalCodeRef.current = unavailable?.code ?? null;
|
|
10774
10820
|
const sessionErrorCodeRef = (0, import_react46.useRef)(null);
|
|
@@ -10785,11 +10831,13 @@ var HeroProductImage = (0, import_react46.memo)(function HeroProductImage2({
|
|
|
10785
10831
|
return;
|
|
10786
10832
|
}
|
|
10787
10833
|
const rendered = renderVersionRef.current;
|
|
10788
|
-
const
|
|
10834
|
+
const versions = renderVersionsRef.current;
|
|
10835
|
+
const sent = versions ? getLatestSentVersions?.() : void 0;
|
|
10836
|
+
const behind = versions && sent ? Object.entries(sent).some(([placement2, v]) => (versions[placement2] ?? -1) < v) : rendered < canvas;
|
|
10789
10837
|
node.dataset.canvasVersion = String(canvas);
|
|
10790
10838
|
node.dataset.renderVersion = String(rendered);
|
|
10791
10839
|
node.dataset.renderState = refusalCodeRef.current || behind && sessionErrorCodeRef.current ? "refused" : behind ? "stale" : "current";
|
|
10792
|
-
}, [getCanvasVersion]);
|
|
10840
|
+
}, [getCanvasVersion, getLatestSentVersions]);
|
|
10793
10841
|
(0, import_react46.useEffect)(() => {
|
|
10794
10842
|
applyRenderStamp();
|
|
10795
10843
|
});
|
|
@@ -10799,8 +10847,9 @@ var HeroProductImage = (0, import_react46.memo)(function HeroProductImage2({
|
|
|
10799
10847
|
if (!actualMockupId || !subscribeSent || !subscribeResult) return;
|
|
10800
10848
|
const offSent = subscribeSent(() => applyRenderStamp());
|
|
10801
10849
|
const offResult = subscribeResult(actualMockupId, (result) => {
|
|
10802
|
-
if (typeof result?.requestVersion === "number" && result.requestVersion
|
|
10850
|
+
if (typeof result?.requestVersion === "number" && result.requestVersion >= renderVersionRef.current) {
|
|
10803
10851
|
renderVersionRef.current = result.requestVersion;
|
|
10852
|
+
renderVersionsRef.current = result.versions ?? null;
|
|
10804
10853
|
}
|
|
10805
10854
|
applyRenderStamp();
|
|
10806
10855
|
});
|