@buildcores/render-client 1.5.0 → 1.6.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.
@@ -35,6 +35,7 @@ export type SpriteRenderInput = {
35
35
  parts: RenderBuildRequest;
36
36
  showGrid?: boolean;
37
37
  cameraOffsetX?: number;
38
+ cameraZoom?: number;
38
39
  gridSettings?: RenderGridSettings;
39
40
  frameQuality?: 'standard' | 'high';
40
41
  } | {
@@ -43,6 +44,7 @@ export type SpriteRenderInput = {
43
44
  profile?: 'cinematic' | 'flat' | 'fast';
44
45
  showGrid?: boolean;
45
46
  cameraOffsetX?: number;
47
+ cameraZoom?: number;
46
48
  gridSettings?: RenderGridSettings;
47
49
  frameQuality?: 'standard' | 'high';
48
50
  };
@@ -4,6 +4,7 @@ interface UseZoomOptions {
4
4
  displayHeight?: number;
5
5
  minScale?: number;
6
6
  maxScale?: number;
7
+ initialScale?: number;
7
8
  }
8
9
  interface UseZoomReturn {
9
10
  scale: number;
@@ -12,5 +13,5 @@ interface UseZoomReturn {
12
13
  handleTouchStart: (event: ReactTouchEvent<HTMLCanvasElement>) => boolean;
13
14
  reset: () => void;
14
15
  }
15
- export declare const useZoomPan: ({ displayWidth, displayHeight, minScale, maxScale, }?: UseZoomOptions) => UseZoomReturn;
16
+ export declare const useZoomPan: ({ displayWidth, displayHeight, minScale, maxScale, initialScale, }?: UseZoomOptions) => UseZoomReturn;
16
17
  export {};
package/dist/index.d.ts CHANGED
@@ -315,6 +315,46 @@ interface BuildRenderProps {
315
315
  * @default "standard"
316
316
  */
317
317
  frameQuality?: 'standard' | 'high';
318
+ /**
319
+ * Initial zoom level for the build.
320
+ * Range: 0.5 (50%) to 2.5 (250%). Values less than 1 make the build appear smaller,
321
+ * values greater than 1 make it appear larger.
322
+ *
323
+ * @example
324
+ * ```tsx
325
+ * <BuildRender
326
+ * shareCode="abc123"
327
+ * zoom={0.7} // 70% size - build appears smaller
328
+ * />
329
+ *
330
+ * <BuildRender
331
+ * shareCode="abc123"
332
+ * zoom={1.5} // 150% size - build appears larger
333
+ * />
334
+ * ```
335
+ *
336
+ * @default 1
337
+ */
338
+ zoom?: number;
339
+ /**
340
+ * Camera zoom level for server-side rendering.
341
+ * Values > 1 move the camera further away (build appears smaller in the sprite).
342
+ * Values < 1 move the camera closer (build appears larger in the sprite).
343
+ *
344
+ * Use this for higher quality scaled-down renders vs client-side zoom scaling.
345
+ * Range: 0.5 to 2.0
346
+ *
347
+ * @example
348
+ * ```tsx
349
+ * <BuildRender
350
+ * shareCode="abc123"
351
+ * cameraZoom={1.3} // Camera 30% further away - smaller build in sprite
352
+ * />
353
+ * ```
354
+ *
355
+ * @default 1
356
+ */
357
+ cameraZoom?: number;
318
358
  }
319
359
  /**
320
360
  * API configuration for environment and authentication
@@ -531,6 +571,15 @@ interface RenderBuildRequest {
531
571
  * @default "standard"
532
572
  */
533
573
  frameQuality?: 'standard' | 'high';
574
+ /**
575
+ * Camera zoom level for server-side rendering.
576
+ * Values > 1 move the camera further away (build appears smaller in the sprite).
577
+ * Values < 1 move the camera closer (build appears larger in the sprite).
578
+ * Range: 0.5 to 2.0
579
+ *
580
+ * @default 1
581
+ */
582
+ cameraZoom?: number;
534
583
  }
535
584
  /**
536
585
  * Response structure containing all available parts for each category.
@@ -674,6 +723,11 @@ interface BuildResponse {
674
723
  partDetails: {
675
724
  [K in PartCategory]?: PartDetailsWithCategory[];
676
725
  };
726
+ /**
727
+ * Whether the case in this build has an interactive 3D model available.
728
+ * If false, the build cannot be rendered in 3D.
729
+ */
730
+ hasInteractiveModel: boolean;
677
731
  }
678
732
  /**
679
733
  * Response from the get parts by IDs endpoint.
@@ -725,6 +779,8 @@ interface RenderByShareCodeOptions {
725
779
  gridSettings?: GridSettings;
726
780
  /** Frame quality - 'standard' (72 frames) or 'high' (144 frames for smoother animation) */
727
781
  frameQuality?: 'standard' | 'high';
782
+ /** Camera zoom level for rendering. Values > 1 move camera further (build appears smaller). Range: 0.5 to 2.0 */
783
+ cameraZoom?: number;
728
784
  /** Polling interval in milliseconds (default: 1500) */
729
785
  pollIntervalMs?: number;
730
786
  /** Timeout in milliseconds (default: 120000 = 2 minutes) */
@@ -867,6 +923,7 @@ type SpriteRenderInput = {
867
923
  parts: RenderBuildRequest;
868
924
  showGrid?: boolean;
869
925
  cameraOffsetX?: number;
926
+ cameraZoom?: number;
870
927
  gridSettings?: RenderGridSettings;
871
928
  frameQuality?: 'standard' | 'high';
872
929
  } | {
@@ -875,6 +932,7 @@ type SpriteRenderInput = {
875
932
  profile?: 'cinematic' | 'flat' | 'fast';
876
933
  showGrid?: boolean;
877
934
  cameraOffsetX?: number;
935
+ cameraZoom?: number;
878
936
  gridSettings?: RenderGridSettings;
879
937
  frameQuality?: 'standard' | 'high';
880
938
  };
package/dist/index.esm.js CHANGED
@@ -268,6 +268,8 @@ const createRenderBuildJob = async (request, config) => {
268
268
  ...(request.gridSettings ? { gridSettings: request.gridSettings } : {}),
269
269
  // Include frame quality for sprite rendering
270
270
  ...(request.frameQuality ? { frameQuality: request.frameQuality } : {}),
271
+ // Include camera zoom for render-time scaling
272
+ ...(request.cameraZoom !== undefined ? { cameraZoom: request.cameraZoom } : {}),
271
273
  };
272
274
  const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD, config), {
273
275
  method: "POST",
@@ -464,6 +466,7 @@ const createRenderByShareCodeJob = async (shareCode, config, options) => {
464
466
  ...(options?.cameraOffsetX !== undefined ? { cameraOffsetX: options.cameraOffsetX } : {}),
465
467
  ...(options?.gridSettings ? { gridSettings: options.gridSettings } : {}),
466
468
  ...(options?.frameQuality ? { frameQuality: options.frameQuality } : {}),
469
+ ...(options?.cameraZoom !== undefined ? { cameraZoom: options.cameraZoom } : {}),
467
470
  };
468
471
  const response = await fetch(url, {
469
472
  method: "POST",
@@ -691,6 +694,7 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
691
694
  profile: currentInput.profile,
692
695
  showGrid: currentInput.showGrid,
693
696
  cameraOffsetX: currentInput.cameraOffsetX,
697
+ cameraZoom: currentInput.cameraZoom,
694
698
  gridSettings: currentInput.gridSettings,
695
699
  frameQuality: currentInput.frameQuality
696
700
  });
@@ -716,6 +720,7 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
716
720
  ...currentParts,
717
721
  showGrid: currentInput.showGrid,
718
722
  cameraOffsetX: currentInput.cameraOffsetX,
723
+ cameraZoom: currentInput.cameraZoom,
719
724
  gridSettings: currentInput.gridSettings,
720
725
  frameQuality,
721
726
  }, apiConfig);
@@ -741,6 +746,7 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
741
746
  format: "sprite",
742
747
  showGrid: currentInput.showGrid,
743
748
  cameraOffsetX: currentInput.cameraOffsetX,
749
+ cameraZoom: currentInput.cameraZoom,
744
750
  gridSettings: currentInput.gridSettings,
745
751
  frameQuality,
746
752
  }, apiConfig);
@@ -774,6 +780,7 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
774
780
  a.profile === b.profile &&
775
781
  a.showGrid === b.showGrid &&
776
782
  a.cameraOffsetX === b.cameraOffsetX &&
783
+ a.cameraZoom === b.cameraZoom &&
777
784
  a.frameQuality === b.frameQuality &&
778
785
  gridSettingsEqual;
779
786
  }
@@ -783,6 +790,7 @@ const useSpriteRender = (input, apiConfig, onLoadStart, options) => {
783
790
  return arePartsEqual(a.parts, b.parts) &&
784
791
  a.showGrid === b.showGrid &&
785
792
  a.cameraOffsetX === b.cameraOffsetX &&
793
+ a.cameraZoom === b.cameraZoom &&
786
794
  a.frameQuality === b.frameQuality &&
787
795
  gridSettingsEqual;
788
796
  }
@@ -875,10 +883,10 @@ const getTouchDistance = (touches) => {
875
883
  return 0;
876
884
  return Math.hypot(second.clientX - first.clientX, second.clientY - first.clientY);
877
885
  };
878
- const useZoomPan = ({ displayWidth, displayHeight, minScale = 1, maxScale = 4, } = {}) => {
879
- const [scale, setScale] = useState(1);
886
+ const useZoomPan = ({ displayWidth, displayHeight, minScale = 0.5, maxScale = 2.5, initialScale = 1, } = {}) => {
887
+ const [scale, setScale] = useState(initialScale);
880
888
  const [isPinching, setIsPinching] = useState(false);
881
- const scaleRef = useRef(1);
889
+ const scaleRef = useRef(initialScale);
882
890
  const pinchDataRef = useRef({
883
891
  initialDistance: 0,
884
892
  initialScale: 1,
@@ -947,9 +955,9 @@ const useZoomPan = ({ displayWidth, displayHeight, minScale = 1, maxScale = 4, }
947
955
  };
948
956
  }, [isPinching, setScaleSafe]);
949
957
  const reset = useCallback(() => {
950
- scaleRef.current = 1;
951
- setScale(1);
952
- }, []);
958
+ scaleRef.current = initialScale;
959
+ setScale(initialScale);
960
+ }, [initialScale]);
953
961
  return {
954
962
  scale,
955
963
  isPinching,
@@ -959,7 +967,7 @@ const useZoomPan = ({ displayWidth, displayHeight, minScale = 1, maxScale = 4, }
959
967
  };
960
968
  };
961
969
 
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, }) => {
970
+ const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpriteRenderOptions, mouseSensitivity = 0.2, touchSensitivity = 0.2, showGrid, cameraOffsetX, cameraZoom, gridSettings, animationMode = 'bounce', spinDuration = 10000, interactive = true, frameQuality, zoom = 1, }) => {
963
971
  const canvasRef = useRef(null);
964
972
  const containerRef = useRef(null);
965
973
  const [img, setImg] = useState(null);
@@ -976,6 +984,7 @@ const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpri
976
984
  profile: parts?.profile,
977
985
  showGrid,
978
986
  cameraOffsetX,
987
+ cameraZoom,
979
988
  gridSettings,
980
989
  frameQuality,
981
990
  };
@@ -985,10 +994,11 @@ const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpri
985
994
  parts: parts,
986
995
  showGrid,
987
996
  cameraOffsetX,
997
+ cameraZoom,
988
998
  gridSettings,
989
999
  frameQuality,
990
1000
  };
991
- }, [shareCode, parts, showGrid, cameraOffsetX, gridSettings, frameQuality]);
1001
+ }, [shareCode, parts, showGrid, cameraOffsetX, cameraZoom, gridSettings, frameQuality]);
992
1002
  // Use custom hook for sprite rendering
993
1003
  const { spriteSrc, isRenderingSprite, renderError, spriteMetadata } = useSpriteRender(renderInput, apiConfig, undefined, useSpriteRenderOptions);
994
1004
  const total = spriteMetadata ? spriteMetadata.totalFrames : 72;
@@ -1001,6 +1011,7 @@ const BuildRender = ({ parts, shareCode, width, height, size, apiConfig, useSpri
1001
1011
  const { scale, handleWheel: handleZoomWheel, handleTouchStart: handleZoomTouchStart, reset: resetZoom, } = useZoomPan({
1002
1012
  displayWidth: displayW,
1003
1013
  displayHeight: displayH,
1014
+ initialScale: zoom,
1004
1015
  });
1005
1016
  // Image/frame sizes - only calculate if image dimensions match expected metadata
1006
1017
  // This prevents using stale image with new metadata during transitions