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