@buildcores/render-client 1.0.8 → 1.0.10

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/dist/index.js CHANGED
@@ -417,7 +417,10 @@ const renderBuild = async (request, config, options) => {
417
417
  for (;;) {
418
418
  const status = await getRenderBuildStatus(job_id, config);
419
419
  if (status.status === "completed") {
420
- const finalUrl = status.url ?? undefined;
420
+ const requestedFormat = request.format ?? "video";
421
+ const finalUrl = (requestedFormat === "sprite"
422
+ ? status.sprite_url || status.url || undefined
423
+ : status.video_url || status.url || undefined);
421
424
  if (!finalUrl) {
422
425
  throw new Error("Render job completed but no URL returned");
423
426
  }
@@ -599,7 +602,7 @@ const useBuildRender = (parts, apiConfig, onLoadStart, options) => {
599
602
  };
600
603
  };
601
604
 
602
- const useSpriteRender = (parts, apiConfig, onLoadStart) => {
605
+ const useSpriteRender = (parts, apiConfig, onLoadStart, options) => {
603
606
  const [spriteSrc, setSpriteSrc] = React.useState(null);
604
607
  const [isRenderingSprite, setIsRenderingSprite] = React.useState(false);
605
608
  const [renderError, setRenderError] = React.useState(null);
@@ -610,21 +613,36 @@ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
610
613
  setIsRenderingSprite(true);
611
614
  setRenderError(null);
612
615
  onLoadStart?.();
613
- const response = await renderSpriteExperimental(currentParts, apiConfig);
614
- const objectUrl = URL.createObjectURL(response.sprite);
615
- // Clean up previous sprite URL before setting new one
616
- setSpriteSrc((prevSrc) => {
617
- if (prevSrc) {
618
- URL.revokeObjectURL(prevSrc);
619
- }
620
- return objectUrl;
621
- });
622
- // Set sprite metadata
623
- setSpriteMetadata({
624
- cols: response.metadata?.cols || 12,
625
- rows: response.metadata?.rows || 6,
626
- totalFrames: response.metadata?.totalFrames || 72,
627
- });
616
+ const mode = options?.mode ?? "async";
617
+ if (mode === "experimental") {
618
+ const response = await renderSpriteExperimental(currentParts, apiConfig);
619
+ const objectUrl = URL.createObjectURL(response.sprite);
620
+ // Clean up previous sprite URL before setting new one
621
+ setSpriteSrc((prevSrc) => {
622
+ if (prevSrc && prevSrc.startsWith("blob:")) {
623
+ URL.revokeObjectURL(prevSrc);
624
+ }
625
+ return objectUrl;
626
+ });
627
+ // Set sprite metadata
628
+ setSpriteMetadata({
629
+ cols: response.metadata?.cols || 12,
630
+ rows: response.metadata?.rows || 6,
631
+ totalFrames: response.metadata?.totalFrames || 72,
632
+ });
633
+ }
634
+ else {
635
+ // Async job-based flow: request sprite format and use returned URL
636
+ const { videoUrl: spriteUrl } = await renderBuild({ ...currentParts, format: "sprite" }, apiConfig);
637
+ setSpriteSrc((prevSrc) => {
638
+ if (prevSrc && prevSrc.startsWith("blob:")) {
639
+ URL.revokeObjectURL(prevSrc);
640
+ }
641
+ return spriteUrl;
642
+ });
643
+ // No metadata from async endpoint; keep defaults
644
+ setSpriteMetadata({ cols: 12, rows: 6, totalFrames: 72 });
645
+ }
628
646
  }
629
647
  catch (error) {
630
648
  setRenderError(error instanceof Error ? error.message : "Failed to render sprite");
@@ -632,7 +650,7 @@ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
632
650
  finally {
633
651
  setIsRenderingSprite(false);
634
652
  }
635
- }, [apiConfig, onLoadStart]);
653
+ }, [apiConfig, onLoadStart, options?.mode]);
636
654
  // Effect to call API when parts content changes (using custom equality check)
637
655
  React.useEffect(() => {
638
656
  const shouldFetch = previousPartsRef.current === null ||
@@ -645,7 +663,7 @@ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
645
663
  // Cleanup effect for component unmount
646
664
  React.useEffect(() => {
647
665
  return () => {
648
- if (spriteSrc) {
666
+ if (spriteSrc && spriteSrc.startsWith("blob:")) {
649
667
  URL.revokeObjectURL(spriteSrc);
650
668
  }
651
669
  };
@@ -713,11 +731,13 @@ const InstructionTooltip = ({ isVisible, progressValue, instructionIcon, }) => {
713
731
  } })) }));
714
732
  };
715
733
 
716
- const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSensitivity = 0.2, }) => {
734
+ const BuildRender = ({ parts, width, height, size, apiConfig, mouseSensitivity = 0.2, touchSensitivity = 0.2, }) => {
717
735
  const canvasRef = React.useRef(null);
718
736
  const [img, setImg] = React.useState(null);
719
737
  const [isLoading, setIsLoading] = React.useState(true);
720
738
  const [bouncingAllowed, setBouncingAllowed] = React.useState(false);
739
+ const displayW = width ?? size ?? 300;
740
+ const displayH = height ?? size ?? 300;
721
741
  // Use custom hook for sprite rendering
722
742
  const { spriteSrc, isRenderingSprite, renderError, spriteMetadata } = useSpriteRender(parts, apiConfig);
723
743
  const { value: progressValue, isBouncing } = useBouncePatternProgress(bouncingAllowed);
@@ -763,8 +783,8 @@ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSens
763
783
  return;
764
784
  // Backing store sized for HiDPI; CSS size stays `size`
765
785
  const dpr = Math.max(1, window.devicePixelRatio || 1);
766
- const targetW = Math.round(size * dpr);
767
- const targetH = Math.round(size * dpr);
786
+ const targetW = Math.round(displayW * dpr);
787
+ const targetH = Math.round(displayH * dpr);
768
788
  if (cnv.width !== targetW || cnv.height !== targetH) {
769
789
  cnv.width = targetW;
770
790
  cnv.height = targetH;
@@ -784,7 +804,7 @@ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSens
784
804
  ctx.imageSmoothingEnabled = true;
785
805
  ctx.imageSmoothingQuality = "high";
786
806
  ctx.drawImage(img, sx, sy, sw, sh, 0, 0, targetW, targetH);
787
- }, [img, frameW, frameH, size, cols, total]);
807
+ }, [img, frameW, frameH, displayW, displayH, cols, total]);
788
808
  const { isDragging, handleMouseDown, handleTouchStart, hasDragged } = useSpriteScrubbing(canvasRef, total, {
789
809
  mouseSensitivity,
790
810
  touchSensitivity,
@@ -814,19 +834,19 @@ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSens
814
834
  }, [img, isLoading, draw]);
815
835
  return (jsxRuntime.jsxs("div", { style: {
816
836
  position: "relative",
817
- width: size,
818
- height: size,
837
+ width: displayW,
838
+ height: displayH,
819
839
  backgroundColor: "black",
820
840
  }, children: [img && (jsxRuntime.jsx("canvas", { ref: canvasRef, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, style: {
821
- width: size,
822
- height: size,
841
+ width: displayW,
842
+ height: displayH,
823
843
  cursor: isDragging ? "grabbing" : "grab",
824
844
  touchAction: "none", // Prevents default touch behaviors like scrolling
825
845
  display: "block",
826
846
  userSelect: "none",
827
847
  WebkitUserSelect: "none",
828
848
  WebkitTouchCallout: "none",
829
- }, role: "img", "aria-label": "360\u00B0 viewer", onContextMenu: (e) => e.preventDefault() })), jsxRuntime.jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingSprite || !!renderError, renderError: renderError || undefined, size: size }), jsxRuntime.jsx(InstructionTooltip, { isVisible: !isLoading &&
849
+ }, role: "img", "aria-label": "360\u00B0 viewer", onContextMenu: (e) => e.preventDefault() })), jsxRuntime.jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingSprite || !!renderError, renderError: renderError || undefined, size: Math.min(displayW, displayH) }), jsxRuntime.jsx(InstructionTooltip, { isVisible: !isLoading &&
830
850
  !isRenderingSprite &&
831
851
  !renderError &&
832
852
  isBouncing &&
@@ -926,10 +946,12 @@ const useVideoScrubbing = (videoRef, options = {}) => {
926
946
  };
927
947
  };
928
948
 
929
- const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, touchSensitivity = 0.01, }) => {
949
+ const BuildRenderVideo = ({ parts, width, height, size, apiConfig, mouseSensitivity = 0.01, touchSensitivity = 0.01, }) => {
930
950
  const videoRef = React.useRef(null);
931
951
  const [isLoading, setIsLoading] = React.useState(true);
932
952
  const [bouncingAllowed, setBouncingAllowed] = React.useState(false);
953
+ const displayW = width ?? size ?? 300;
954
+ const displayH = height ?? size ?? 300;
933
955
  // Use custom hook for build rendering
934
956
  const { videoSrc, isRenderingBuild, renderError } = useBuildRender(parts, apiConfig);
935
957
  const { value: progressValue, isBouncing } = useBouncePatternProgress(bouncingAllowed);
@@ -959,7 +981,7 @@ const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, tou
959
981
  videoRef.current.currentTime = time;
960
982
  }
961
983
  }, [progressValue, hasDragged]);
962
- return (jsxRuntime.jsxs("div", { style: { position: "relative", width: size, height: size }, children: [videoSrc && (jsxRuntime.jsx("video", { ref: videoRef, src: videoSrc, width: size, height: size, autoPlay: true, preload: "metadata", muted: true, playsInline: true, controls: false, disablePictureInPicture: true, controlsList: "nodownload nofullscreen noremoteplayback", ...{ "x-webkit-airplay": "deny" }, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, onLoadStart: handleLoadStartInternal, onCanPlay: handleCanPlayInternal, onLoadedData: () => {
984
+ return (jsxRuntime.jsxs("div", { style: { position: "relative", width: displayW, height: displayH }, children: [videoSrc && (jsxRuntime.jsx("video", { ref: videoRef, src: videoSrc, width: displayW, height: displayH, autoPlay: true, preload: "metadata", muted: true, playsInline: true, controls: false, disablePictureInPicture: true, controlsList: "nodownload nofullscreen noremoteplayback", ...{ "x-webkit-airplay": "deny" }, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, onLoadStart: handleLoadStartInternal, onCanPlay: handleCanPlayInternal, onLoadedData: () => {
963
985
  if (videoRef.current) {
964
986
  videoRef.current.pause();
965
987
  }
@@ -977,7 +999,7 @@ const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, tou
977
999
  WebkitTouchCallout: "none",
978
1000
  WebkitUserSelect: "none",
979
1001
  userSelect: "none",
980
- }, children: "Your browser does not support the video tag." }, videoSrc)), jsxRuntime.jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingBuild || !!renderError, renderError: renderError || undefined, size: size }), jsxRuntime.jsx(InstructionTooltip, { isVisible: !isLoading &&
1002
+ }, children: "Your browser does not support the video tag." }, videoSrc)), jsxRuntime.jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingBuild || !!renderError, renderError: renderError || undefined, size: Math.min(displayW, displayH) }), jsxRuntime.jsx(InstructionTooltip, { isVisible: !isLoading &&
981
1003
  !isRenderingBuild &&
982
1004
  !renderError &&
983
1005
  isBouncing &&