@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/api.d.ts +2 -0
- package/dist/hooks/useSpriteRender.d.ts +9 -1
- package/dist/index.d.ts +20 -31
- package/dist/index.esm.js +53 -31
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +53 -31
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +10 -29
- package/package.json +1 -1
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
|
|
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
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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(
|
|
767
|
-
const targetH = Math.round(
|
|
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,
|
|
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:
|
|
818
|
-
height:
|
|
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:
|
|
822
|
-
height:
|
|
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:
|
|
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:
|
|
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:
|
|
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 &&
|