@buildcores/render-client 1.5.0 → 1.7.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/README.md +34 -0
- package/dist/api.d.ts +2 -1
- package/dist/hooks/useSpriteRender.d.ts +10 -0
- package/dist/hooks/useZoomPan.d.ts +2 -1
- package/dist/index.d.ts +146 -2
- package/dist/index.esm.js +169 -47
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +169 -47
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +120 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -213,17 +213,73 @@ const buildApiUrl = (endpoint, config) => {
|
|
|
213
213
|
}
|
|
214
214
|
return baseUrl;
|
|
215
215
|
};
|
|
216
|
+
const SESSION_TOKEN_REFRESH_WINDOW_MS = 15000;
|
|
217
|
+
const sessionTokenCache = new WeakMap();
|
|
218
|
+
const resolveAuthMode = (config) => config.authMode ?? (config.getRenderSessionToken ? "session" : "legacy");
|
|
219
|
+
const isSessionAuthMode = (config) => resolveAuthMode(config) === "session";
|
|
220
|
+
const parseExpiresAtMs = (expiresAt) => {
|
|
221
|
+
const parsed = Date.parse(expiresAt);
|
|
222
|
+
if (!Number.isFinite(parsed)) {
|
|
223
|
+
throw new Error("Invalid session token expiry (expiresAt)");
|
|
224
|
+
}
|
|
225
|
+
return parsed;
|
|
226
|
+
};
|
|
227
|
+
const resolveSessionToken = async (config, forceRefresh) => {
|
|
228
|
+
const supplier = config.getRenderSessionToken;
|
|
229
|
+
if (!supplier) {
|
|
230
|
+
throw new Error("authMode=session requires getRenderSessionToken");
|
|
231
|
+
}
|
|
232
|
+
const cached = sessionTokenCache.get(supplier);
|
|
233
|
+
const now = Date.now();
|
|
234
|
+
if (!forceRefresh &&
|
|
235
|
+
cached &&
|
|
236
|
+
cached.expiresAtMs - SESSION_TOKEN_REFRESH_WINDOW_MS > now) {
|
|
237
|
+
return cached.token;
|
|
238
|
+
}
|
|
239
|
+
const session = await supplier();
|
|
240
|
+
if (!session?.token || !session?.expiresAt) {
|
|
241
|
+
throw new Error("getRenderSessionToken must return { token, expiresAt }");
|
|
242
|
+
}
|
|
243
|
+
const expiresAtMs = parseExpiresAtMs(session.expiresAt);
|
|
244
|
+
sessionTokenCache.set(supplier, {
|
|
245
|
+
token: session.token,
|
|
246
|
+
expiresAtMs,
|
|
247
|
+
});
|
|
248
|
+
return session.token;
|
|
249
|
+
};
|
|
250
|
+
const resolveAuthToken = async (config, forceRefresh) => {
|
|
251
|
+
if (isSessionAuthMode(config)) {
|
|
252
|
+
return resolveSessionToken(config, forceRefresh);
|
|
253
|
+
}
|
|
254
|
+
return config.authToken;
|
|
255
|
+
};
|
|
216
256
|
// Helper to build request headers with auth token
|
|
217
|
-
const buildHeaders = (config) => {
|
|
257
|
+
const buildHeaders = (config, authTokenOverride) => {
|
|
218
258
|
const headers = {
|
|
219
259
|
"Content-Type": "application/json",
|
|
220
260
|
accept: "application/json",
|
|
221
261
|
};
|
|
222
|
-
|
|
223
|
-
|
|
262
|
+
const authToken = authTokenOverride ?? config.authToken;
|
|
263
|
+
if (authToken) {
|
|
264
|
+
headers["Authorization"] = `Bearer ${authToken}`;
|
|
224
265
|
}
|
|
225
266
|
return headers;
|
|
226
267
|
};
|
|
268
|
+
const fetchWithApiAuth = async (url, init, config) => {
|
|
269
|
+
const firstToken = await resolveAuthToken(config, false);
|
|
270
|
+
const firstResponse = await fetch(url, {
|
|
271
|
+
...init,
|
|
272
|
+
headers: buildHeaders(config, firstToken),
|
|
273
|
+
});
|
|
274
|
+
if (!isSessionAuthMode(config) || firstResponse.status !== 401) {
|
|
275
|
+
return firstResponse;
|
|
276
|
+
}
|
|
277
|
+
const refreshedToken = await resolveAuthToken(config, true);
|
|
278
|
+
return fetch(url, {
|
|
279
|
+
...init,
|
|
280
|
+
headers: buildHeaders(config, refreshedToken),
|
|
281
|
+
});
|
|
282
|
+
};
|
|
227
283
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
228
284
|
// API Implementation
|
|
229
285
|
const renderBuildExperimental = async (request, config) => {
|
|
@@ -235,12 +291,16 @@ const renderBuildExperimental = async (request, config) => {
|
|
|
235
291
|
...(request.height !== undefined ? { height: request.height } : {}),
|
|
236
292
|
// Include profile if provided
|
|
237
293
|
...(request.profile ? { profile: request.profile } : {}),
|
|
294
|
+
...(request.scene ? { scene: request.scene } : {}),
|
|
295
|
+
...(request.showBackground !== undefined ? { showBackground: request.showBackground } : {}),
|
|
296
|
+
...(request.showGrid !== undefined ? { showGrid: request.showGrid } : {}),
|
|
297
|
+
...(request.winterMode !== undefined ? { winterMode: request.winterMode } : {}),
|
|
298
|
+
...(request.springMode !== undefined ? { springMode: request.springMode } : {}),
|
|
238
299
|
};
|
|
239
|
-
const response = await
|
|
300
|
+
const response = await fetchWithApiAuth(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL, config), {
|
|
240
301
|
method: "POST",
|
|
241
|
-
headers: buildHeaders(config),
|
|
242
302
|
body: JSON.stringify(requestWithFormat),
|
|
243
|
-
});
|
|
303
|
+
}, config);
|
|
244
304
|
if (!response.ok) {
|
|
245
305
|
throw new Error(`Render build failed: ${response.status} ${response.statusText}`);
|
|
246
306
|
}
|
|
@@ -264,18 +324,23 @@ const createRenderBuildJob = async (request, config) => {
|
|
|
264
324
|
...(request.height !== undefined ? { height: request.height } : {}),
|
|
265
325
|
// Include profile if provided
|
|
266
326
|
...(request.profile ? { profile: request.profile } : {}),
|
|
327
|
+
...(request.scene ? { scene: request.scene } : {}),
|
|
328
|
+
...(request.showBackground !== undefined ? { showBackground: request.showBackground } : {}),
|
|
267
329
|
// Include composition settings
|
|
268
330
|
...(request.showGrid !== undefined ? { showGrid: request.showGrid } : {}),
|
|
331
|
+
...(request.winterMode !== undefined ? { winterMode: request.winterMode } : {}),
|
|
332
|
+
...(request.springMode !== undefined ? { springMode: request.springMode } : {}),
|
|
269
333
|
...(request.cameraOffsetX !== undefined ? { cameraOffsetX: request.cameraOffsetX } : {}),
|
|
270
334
|
...(request.gridSettings ? { gridSettings: request.gridSettings } : {}),
|
|
271
335
|
// Include frame quality for sprite rendering
|
|
272
336
|
...(request.frameQuality ? { frameQuality: request.frameQuality } : {}),
|
|
337
|
+
// Include camera zoom for render-time scaling
|
|
338
|
+
...(request.cameraZoom !== undefined ? { cameraZoom: request.cameraZoom } : {}),
|
|
273
339
|
};
|
|
274
|
-
const response = await
|
|
340
|
+
const response = await fetchWithApiAuth(buildApiUrl(API_ENDPOINTS.RENDER_BUILD, config), {
|
|
275
341
|
method: "POST",
|
|
276
|
-
headers: buildHeaders(config),
|
|
277
342
|
body: JSON.stringify(body),
|
|
278
|
-
});
|
|
343
|
+
}, config);
|
|
279
344
|
if (!response.ok) {
|
|
280
345
|
throw new Error(`Create render job failed: ${response.status} ${response.statusText}`);
|
|
281
346
|
}
|
|
@@ -287,10 +352,7 @@ const createRenderBuildJob = async (request, config) => {
|
|
|
287
352
|
};
|
|
288
353
|
const getRenderBuildStatus = async (jobId, config) => {
|
|
289
354
|
const url = buildApiUrl(`${API_ENDPOINTS.RENDER_BUILD}/${encodeURIComponent(jobId)}`, config);
|
|
290
|
-
const response = await
|
|
291
|
-
method: "GET",
|
|
292
|
-
headers: buildHeaders(config),
|
|
293
|
-
});
|
|
355
|
+
const response = await fetchWithApiAuth(url, { method: "GET" }, config);
|
|
294
356
|
if (response.status === 404) {
|
|
295
357
|
throw new Error("Render job not found");
|
|
296
358
|
}
|
|
@@ -335,12 +397,16 @@ const renderSpriteExperimental = async (request, config) => {
|
|
|
335
397
|
...(request.height !== undefined ? { height: request.height } : {}),
|
|
336
398
|
// Include profile if provided
|
|
337
399
|
...(request.profile ? { profile: request.profile } : {}),
|
|
400
|
+
...(request.scene ? { scene: request.scene } : {}),
|
|
401
|
+
...(request.showBackground !== undefined ? { showBackground: request.showBackground } : {}),
|
|
402
|
+
...(request.showGrid !== undefined ? { showGrid: request.showGrid } : {}),
|
|
403
|
+
...(request.winterMode !== undefined ? { winterMode: request.winterMode } : {}),
|
|
404
|
+
...(request.springMode !== undefined ? { springMode: request.springMode } : {}),
|
|
338
405
|
};
|
|
339
|
-
const response = await
|
|
406
|
+
const response = await fetchWithApiAuth(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL, config), {
|
|
340
407
|
method: "POST",
|
|
341
|
-
headers: buildHeaders(config),
|
|
342
408
|
body: JSON.stringify(requestWithFormat),
|
|
343
|
-
});
|
|
409
|
+
}, config);
|
|
344
410
|
if (!response.ok) {
|
|
345
411
|
throw new Error(`Render sprite failed: ${response.status} ${response.statusText}`);
|
|
346
412
|
}
|
|
@@ -366,10 +432,7 @@ const getAvailableParts = async (category, config, options) => {
|
|
|
366
432
|
params.set("skip", String(options.skip));
|
|
367
433
|
const separator = base.includes("?") ? "&" : "?";
|
|
368
434
|
const url = `${base}${separator}${params.toString()}`;
|
|
369
|
-
const response = await
|
|
370
|
-
method: "GET",
|
|
371
|
-
headers: buildHeaders(config),
|
|
372
|
-
});
|
|
435
|
+
const response = await fetchWithApiAuth(url, { method: "GET" }, config);
|
|
373
436
|
if (!response.ok) {
|
|
374
437
|
throw new Error(`Get available parts failed: ${response.status} ${response.statusText}`);
|
|
375
438
|
}
|
|
@@ -402,10 +465,7 @@ const getAvailableParts = async (category, config, options) => {
|
|
|
402
465
|
*/
|
|
403
466
|
const getBuildByShareCode = async (shareCode, config) => {
|
|
404
467
|
const url = buildApiUrl(`${API_ENDPOINTS.BUILD}/${encodeURIComponent(shareCode)}`, config);
|
|
405
|
-
const response = await
|
|
406
|
-
method: "GET",
|
|
407
|
-
headers: buildHeaders(config),
|
|
408
|
-
});
|
|
468
|
+
const response = await fetchWithApiAuth(url, { method: "GET" }, config);
|
|
409
469
|
if (response.status === 404) {
|
|
410
470
|
throw new Error("Build not found");
|
|
411
471
|
}
|
|
@@ -435,11 +495,10 @@ const getBuildByShareCode = async (shareCode, config) => {
|
|
|
435
495
|
*/
|
|
436
496
|
const getPartsByIds = async (partIds, config) => {
|
|
437
497
|
const url = buildApiUrl(API_ENDPOINTS.PARTS, config);
|
|
438
|
-
const response = await
|
|
498
|
+
const response = await fetchWithApiAuth(url, {
|
|
439
499
|
method: "POST",
|
|
440
|
-
headers: buildHeaders(config),
|
|
441
500
|
body: JSON.stringify({ ids: partIds }),
|
|
442
|
-
});
|
|
501
|
+
}, config);
|
|
443
502
|
if (!response.ok) {
|
|
444
503
|
throw new Error(`Get parts by IDs failed: ${response.status} ${response.statusText}`);
|
|
445
504
|
}
|
|
@@ -462,16 +521,20 @@ const createRenderByShareCodeJob = async (shareCode, config, options) => {
|
|
|
462
521
|
...(options?.width !== undefined ? { width: options.width } : {}),
|
|
463
522
|
...(options?.height !== undefined ? { height: options.height } : {}),
|
|
464
523
|
...(options?.profile ? { profile: options.profile } : {}),
|
|
524
|
+
...(options?.scene ? { scene: options.scene } : {}),
|
|
525
|
+
...(options?.showBackground !== undefined ? { showBackground: options.showBackground } : {}),
|
|
465
526
|
...(options?.showGrid !== undefined ? { showGrid: options.showGrid } : {}),
|
|
527
|
+
...(options?.winterMode !== undefined ? { winterMode: options.winterMode } : {}),
|
|
528
|
+
...(options?.springMode !== undefined ? { springMode: options.springMode } : {}),
|
|
466
529
|
...(options?.cameraOffsetX !== undefined ? { cameraOffsetX: options.cameraOffsetX } : {}),
|
|
467
530
|
...(options?.gridSettings ? { gridSettings: options.gridSettings } : {}),
|
|
468
531
|
...(options?.frameQuality ? { frameQuality: options.frameQuality } : {}),
|
|
532
|
+
...(options?.cameraZoom !== undefined ? { cameraZoom: options.cameraZoom } : {}),
|
|
469
533
|
};
|
|
470
|
-
const response = await
|
|
534
|
+
const response = await fetchWithApiAuth(url, {
|
|
471
535
|
method: "POST",
|
|
472
|
-
headers: buildHeaders(config),
|
|
473
536
|
body: JSON.stringify(body),
|
|
474
|
-
});
|
|
537
|
+
}, config);
|
|
475
538
|
if (response.status === 404) {
|
|
476
539
|
throw new Error("Build not found");
|
|
477
540
|
}
|
|
@@ -680,7 +743,21 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
|
|
|
680
743
|
const [spriteMetadata, setSpriteMetadata] = react.useState(null);
|
|
681
744
|
const previousInputRef = react.useRef(null);
|
|
682
745
|
// Normalize input to SpriteRenderInput format
|
|
683
|
-
const normalizedInput = 'type' in input
|
|
746
|
+
const normalizedInput = 'type' in input
|
|
747
|
+
? input
|
|
748
|
+
: {
|
|
749
|
+
type: 'parts',
|
|
750
|
+
parts: input,
|
|
751
|
+
showGrid: input.showGrid,
|
|
752
|
+
scene: input.scene,
|
|
753
|
+
showBackground: input.showBackground,
|
|
754
|
+
winterMode: input.winterMode,
|
|
755
|
+
springMode: input.springMode,
|
|
756
|
+
cameraOffsetX: input.cameraOffsetX,
|
|
757
|
+
cameraZoom: input.cameraZoom,
|
|
758
|
+
gridSettings: input.gridSettings,
|
|
759
|
+
frameQuality: input.frameQuality
|
|
760
|
+
};
|
|
684
761
|
const fetchRenderSprite = react.useCallback(async (currentInput) => {
|
|
685
762
|
try {
|
|
686
763
|
setIsRenderingSprite(true);
|
|
@@ -692,7 +769,12 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
|
|
|
692
769
|
format: 'sprite',
|
|
693
770
|
profile: currentInput.profile,
|
|
694
771
|
showGrid: currentInput.showGrid,
|
|
772
|
+
scene: currentInput.scene,
|
|
773
|
+
showBackground: currentInput.showBackground,
|
|
774
|
+
winterMode: currentInput.winterMode,
|
|
775
|
+
springMode: currentInput.springMode,
|
|
695
776
|
cameraOffsetX: currentInput.cameraOffsetX,
|
|
777
|
+
cameraZoom: currentInput.cameraZoom,
|
|
696
778
|
gridSettings: currentInput.gridSettings,
|
|
697
779
|
frameQuality: currentInput.frameQuality
|
|
698
780
|
});
|
|
@@ -717,7 +799,12 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
|
|
|
717
799
|
const response = await renderSpriteExperimental({
|
|
718
800
|
...currentParts,
|
|
719
801
|
showGrid: currentInput.showGrid,
|
|
802
|
+
scene: currentInput.scene,
|
|
803
|
+
showBackground: currentInput.showBackground,
|
|
804
|
+
winterMode: currentInput.winterMode,
|
|
805
|
+
springMode: currentInput.springMode,
|
|
720
806
|
cameraOffsetX: currentInput.cameraOffsetX,
|
|
807
|
+
cameraZoom: currentInput.cameraZoom,
|
|
721
808
|
gridSettings: currentInput.gridSettings,
|
|
722
809
|
frameQuality,
|
|
723
810
|
}, apiConfig);
|
|
@@ -742,7 +829,12 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
|
|
|
742
829
|
...currentParts,
|
|
743
830
|
format: "sprite",
|
|
744
831
|
showGrid: currentInput.showGrid,
|
|
832
|
+
scene: currentInput.scene,
|
|
833
|
+
showBackground: currentInput.showBackground,
|
|
834
|
+
winterMode: currentInput.winterMode,
|
|
835
|
+
springMode: currentInput.springMode,
|
|
745
836
|
cameraOffsetX: currentInput.cameraOffsetX,
|
|
837
|
+
cameraZoom: currentInput.cameraZoom,
|
|
746
838
|
gridSettings: currentInput.gridSettings,
|
|
747
839
|
frameQuality,
|
|
748
840
|
}, apiConfig);
|
|
@@ -775,7 +867,12 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
|
|
|
775
867
|
return a.shareCode === b.shareCode &&
|
|
776
868
|
a.profile === b.profile &&
|
|
777
869
|
a.showGrid === b.showGrid &&
|
|
870
|
+
a.scene === b.scene &&
|
|
871
|
+
a.showBackground === b.showBackground &&
|
|
872
|
+
a.winterMode === b.winterMode &&
|
|
873
|
+
a.springMode === b.springMode &&
|
|
778
874
|
a.cameraOffsetX === b.cameraOffsetX &&
|
|
875
|
+
a.cameraZoom === b.cameraZoom &&
|
|
779
876
|
a.frameQuality === b.frameQuality &&
|
|
780
877
|
gridSettingsEqual;
|
|
781
878
|
}
|
|
@@ -784,7 +881,12 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
|
|
|
784
881
|
const gridSettingsEqual = JSON.stringify(a.gridSettings ?? {}) === JSON.stringify(b.gridSettings ?? {});
|
|
785
882
|
return arePartsEqual(a.parts, b.parts) &&
|
|
786
883
|
a.showGrid === b.showGrid &&
|
|
884
|
+
a.scene === b.scene &&
|
|
885
|
+
a.showBackground === b.showBackground &&
|
|
886
|
+
a.winterMode === b.winterMode &&
|
|
887
|
+
a.springMode === b.springMode &&
|
|
787
888
|
a.cameraOffsetX === b.cameraOffsetX &&
|
|
889
|
+
a.cameraZoom === b.cameraZoom &&
|
|
788
890
|
a.frameQuality === b.frameQuality &&
|
|
789
891
|
gridSettingsEqual;
|
|
790
892
|
}
|
|
@@ -877,10 +979,10 @@ const getTouchDistance = (touches) => {
|
|
|
877
979
|
return 0;
|
|
878
980
|
return Math.hypot(second.clientX - first.clientX, second.clientY - first.clientY);
|
|
879
981
|
};
|
|
880
|
-
const useZoomPan = ({ displayWidth, displayHeight, minScale =
|
|
881
|
-
const [scale, setScale] = react.useState(
|
|
982
|
+
const useZoomPan = ({ displayWidth, displayHeight, minScale = 0.5, maxScale = 2.5, initialScale = 1, } = {}) => {
|
|
983
|
+
const [scale, setScale] = react.useState(initialScale);
|
|
882
984
|
const [isPinching, setIsPinching] = react.useState(false);
|
|
883
|
-
const scaleRef = react.useRef(
|
|
985
|
+
const scaleRef = react.useRef(initialScale);
|
|
884
986
|
const pinchDataRef = react.useRef({
|
|
885
987
|
initialDistance: 0,
|
|
886
988
|
initialScale: 1,
|
|
@@ -949,9 +1051,9 @@ const useZoomPan = ({ displayWidth, displayHeight, minScale = 1, maxScale = 4, }
|
|
|
949
1051
|
};
|
|
950
1052
|
}, [isPinching, setScaleSafe]);
|
|
951
1053
|
const reset = react.useCallback(() => {
|
|
952
|
-
scaleRef.current =
|
|
953
|
-
setScale(
|
|
954
|
-
}, []);
|
|
1054
|
+
scaleRef.current = initialScale;
|
|
1055
|
+
setScale(initialScale);
|
|
1056
|
+
}, [initialScale]);
|
|
955
1057
|
return {
|
|
956
1058
|
scale,
|
|
957
1059
|
isPinching,
|
|
@@ -961,7 +1063,7 @@ const useZoomPan = ({ displayWidth, displayHeight, minScale = 1, maxScale = 4, }
|
|
|
961
1063
|
};
|
|
962
1064
|
};
|
|
963
1065
|
|
|
964
|
-
const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpriteRenderOptions, mouseSensitivity = 0.2, touchSensitivity = 0.2, showGrid, cameraOffsetX, gridSettings, animationMode = 'bounce', spinDuration = 10000, interactive = true, frameQuality, }) => {
|
|
1066
|
+
const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpriteRenderOptions, mouseSensitivity = 0.2, touchSensitivity = 0.2, showGrid, scene, showBackground, winterMode, springMode, cameraOffsetX, cameraZoom, gridSettings, animationMode = 'bounce', spinDuration = 10000, interactive = true, frameQuality, zoom = 1, }) => {
|
|
965
1067
|
const canvasRef = react.useRef(null);
|
|
966
1068
|
const containerRef = react.useRef(null);
|
|
967
1069
|
const [img, setImg] = react.useState(null);
|
|
@@ -971,26 +1073,45 @@ const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpri
|
|
|
971
1073
|
const displayH = height ?? size ?? 300;
|
|
972
1074
|
// Build the render input - prefer shareCode if provided (preserves interactive state like case fan slots)
|
|
973
1075
|
const renderInput = react.useMemo(() => {
|
|
1076
|
+
const resolvedShowGrid = showGrid ?? parts?.showGrid;
|
|
1077
|
+
const resolvedScene = scene ?? parts?.scene;
|
|
1078
|
+
const resolvedShowBackground = showBackground ?? parts?.showBackground;
|
|
1079
|
+
const resolvedWinterMode = winterMode ?? parts?.winterMode;
|
|
1080
|
+
const resolvedSpringMode = springMode ?? parts?.springMode;
|
|
1081
|
+
const resolvedCameraOffsetX = cameraOffsetX ?? parts?.cameraOffsetX;
|
|
1082
|
+
const resolvedCameraZoom = cameraZoom ?? parts?.cameraZoom;
|
|
1083
|
+
const resolvedGridSettings = gridSettings ?? parts?.gridSettings;
|
|
1084
|
+
const resolvedFrameQuality = frameQuality ?? parts?.frameQuality;
|
|
974
1085
|
if (shareCode) {
|
|
975
1086
|
return {
|
|
976
1087
|
type: 'shareCode',
|
|
977
1088
|
shareCode,
|
|
978
1089
|
profile: parts?.profile,
|
|
979
|
-
showGrid,
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1090
|
+
showGrid: resolvedShowGrid,
|
|
1091
|
+
scene: resolvedScene,
|
|
1092
|
+
showBackground: resolvedShowBackground,
|
|
1093
|
+
winterMode: resolvedWinterMode,
|
|
1094
|
+
springMode: resolvedSpringMode,
|
|
1095
|
+
cameraOffsetX: resolvedCameraOffsetX,
|
|
1096
|
+
cameraZoom: resolvedCameraZoom,
|
|
1097
|
+
gridSettings: resolvedGridSettings,
|
|
1098
|
+
frameQuality: resolvedFrameQuality,
|
|
983
1099
|
};
|
|
984
1100
|
}
|
|
985
1101
|
return {
|
|
986
1102
|
type: 'parts',
|
|
987
1103
|
parts: parts,
|
|
988
|
-
showGrid,
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1104
|
+
showGrid: resolvedShowGrid,
|
|
1105
|
+
scene: resolvedScene,
|
|
1106
|
+
showBackground: resolvedShowBackground,
|
|
1107
|
+
winterMode: resolvedWinterMode,
|
|
1108
|
+
springMode: resolvedSpringMode,
|
|
1109
|
+
cameraOffsetX: resolvedCameraOffsetX,
|
|
1110
|
+
cameraZoom: resolvedCameraZoom,
|
|
1111
|
+
gridSettings: resolvedGridSettings,
|
|
1112
|
+
frameQuality: resolvedFrameQuality,
|
|
992
1113
|
};
|
|
993
|
-
}, [shareCode, parts, showGrid, cameraOffsetX, gridSettings, frameQuality]);
|
|
1114
|
+
}, [shareCode, parts, showGrid, scene, showBackground, winterMode, springMode, cameraOffsetX, cameraZoom, gridSettings, frameQuality]);
|
|
994
1115
|
// Use custom hook for sprite rendering
|
|
995
1116
|
const { spriteSrc, isRenderingSprite, renderError, spriteMetadata } = useSpriteRender(renderInput, apiConfig, undefined, useSpriteRenderOptions);
|
|
996
1117
|
const total = spriteMetadata ? spriteMetadata.totalFrames : 72;
|
|
@@ -1003,6 +1124,7 @@ const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpri
|
|
|
1003
1124
|
const { scale, handleWheel: handleZoomWheel, handleTouchStart: handleZoomTouchStart, reset: resetZoom, } = useZoomPan({
|
|
1004
1125
|
displayWidth: displayW,
|
|
1005
1126
|
displayHeight: displayH,
|
|
1127
|
+
initialScale: zoom,
|
|
1006
1128
|
});
|
|
1007
1129
|
// Image/frame sizes - only calculate if image dimensions match expected metadata
|
|
1008
1130
|
// This prevents using stale image with new metadata during transitions
|