@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/api.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface RenderJobStatusResponse {
|
|
|
27
27
|
job_id: string;
|
|
28
28
|
status: "queued" | "processing" | "completed" | "error";
|
|
29
29
|
url?: string | null;
|
|
30
|
+
video_url?: string | null;
|
|
31
|
+
sprite_url?: string | null;
|
|
30
32
|
error?: string | null;
|
|
31
33
|
end_time?: string | null;
|
|
32
34
|
}
|
|
@@ -9,4 +9,12 @@ export interface UseSpriteRenderReturn {
|
|
|
9
9
|
totalFrames: number;
|
|
10
10
|
} | null;
|
|
11
11
|
}
|
|
12
|
-
export
|
|
12
|
+
export interface UseSpriteRenderOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Choose which backend flow to use
|
|
15
|
+
* - 'async' (default): uses /render-build and polls /render-build/{jobId} with format 'sprite'
|
|
16
|
+
* - 'experimental': uses /render-build-experimental and returns Blob
|
|
17
|
+
*/
|
|
18
|
+
mode?: "async" | "experimental";
|
|
19
|
+
}
|
|
20
|
+
export declare const useSpriteRender: (parts: RenderBuildRequest, apiConfig: ApiConfig, onLoadStart?: () => void, options?: UseSpriteRenderOptions) => UseSpriteRenderReturn;
|
package/dist/index.d.ts
CHANGED
|
@@ -45,24 +45,12 @@ interface BuildRenderVideoProps {
|
|
|
45
45
|
*/
|
|
46
46
|
parts: RenderBuildRequest;
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* This determines the resolution of the rendered 3D video. Higher values
|
|
51
|
-
* provide better quality but may impact performance.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```tsx
|
|
55
|
-
* <BuildRender parts={parts} size={300} /> // 300x300px
|
|
56
|
-
* <BuildRender parts={parts} size={500} /> // 500x500px
|
|
57
|
-
* <BuildRender parts={parts} size={800} /> // 800x800px - high quality
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* Recommended sizes:
|
|
61
|
-
* - 300px: Good for thumbnails or small previews
|
|
62
|
-
* - 500px: Standard size for most use cases
|
|
63
|
-
* - 800px+: High quality for detailed viewing
|
|
48
|
+
* Width and height in pixels. If only `size` is provided, both width and height use it.
|
|
49
|
+
* If `width`/`height` are provided, they override `size` individually.
|
|
64
50
|
*/
|
|
65
|
-
|
|
51
|
+
width?: number;
|
|
52
|
+
height?: number;
|
|
53
|
+
size?: number;
|
|
66
54
|
/**
|
|
67
55
|
* API configuration for environment and authentication.
|
|
68
56
|
* This is required to make API calls to the BuildCores rendering service.
|
|
@@ -159,19 +147,12 @@ interface BuildRenderProps {
|
|
|
159
147
|
*/
|
|
160
148
|
parts: RenderBuildRequest;
|
|
161
149
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* This determines the display size of the rendered 3D sprite. The sprite sheet
|
|
165
|
-
* itself is rendered at a fixed resolution, but this controls the display size.
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* ```tsx
|
|
169
|
-
* <SpriteRender parts={parts} size={300} /> // 300x300px
|
|
170
|
-
* <SpriteRender parts={parts} size={500} /> // 500x500px
|
|
171
|
-
* <SpriteRender parts={parts} size={800} /> // 800x800px - larger display
|
|
172
|
-
* ```
|
|
150
|
+
* Width and height in pixels. If only `size` is provided, both width and height use it.
|
|
151
|
+
* If `width`/`height` are provided, they override `size` individually.
|
|
173
152
|
*/
|
|
174
|
-
|
|
153
|
+
width?: number;
|
|
154
|
+
height?: number;
|
|
155
|
+
size?: number;
|
|
175
156
|
/**
|
|
176
157
|
* API configuration for environment and authentication.
|
|
177
158
|
* This is required to make API calls to the BuildCores rendering service.
|
|
@@ -475,7 +456,15 @@ interface UseSpriteRenderReturn {
|
|
|
475
456
|
totalFrames: number;
|
|
476
457
|
} | null;
|
|
477
458
|
}
|
|
478
|
-
|
|
459
|
+
interface UseSpriteRenderOptions {
|
|
460
|
+
/**
|
|
461
|
+
* Choose which backend flow to use
|
|
462
|
+
* - 'async' (default): uses /render-build and polls /render-build/{jobId} with format 'sprite'
|
|
463
|
+
* - 'experimental': uses /render-build-experimental and returns Blob
|
|
464
|
+
*/
|
|
465
|
+
mode?: "async" | "experimental";
|
|
466
|
+
}
|
|
467
|
+
declare const useSpriteRender: (parts: RenderBuildRequest, apiConfig: ApiConfig, onLoadStart?: () => void, options?: UseSpriteRenderOptions) => UseSpriteRenderReturn;
|
|
479
468
|
|
|
480
469
|
interface DragIconProps {
|
|
481
470
|
width?: number;
|
|
@@ -557,4 +546,4 @@ declare const renderSpriteExperimental: (request: RenderBuildRequest, config: Ap
|
|
|
557
546
|
declare const getAvailableParts: (config: ApiConfig) => Promise<AvailablePartsResponse>;
|
|
558
547
|
|
|
559
548
|
export { API_BASE_URL, API_ENDPOINTS, BuildRender, BuildRenderVideo, DragIcon, InstructionTooltip, LoadingErrorOverlay, PartCategory, arePartsEqual, buildApiUrl, buildHeaders, calculateCircularFrame, calculateCircularTime, getAvailableParts, renderBuildExperimental, renderSpriteExperimental, useBouncePatternProgress, useBuildRender, useSpriteRender, useSpriteScrubbing, useVideoScrubbing };
|
|
560
|
-
export type { ApiConfig, AvailablePartsResponse, BuildRenderProps, BuildRenderVideoProps, PartDetails, RenderAPIService, RenderBuildRequest, RenderBuildResponse, RenderSpriteResponse, UseBuildRenderOptions, UseBuildRenderReturn, UseSpriteRenderReturn };
|
|
549
|
+
export type { ApiConfig, AvailablePartsResponse, BuildRenderProps, BuildRenderVideoProps, PartDetails, RenderAPIService, RenderBuildRequest, RenderBuildResponse, RenderSpriteResponse, UseBuildRenderOptions, UseBuildRenderReturn, UseSpriteRenderOptions, UseSpriteRenderReturn };
|
package/dist/index.esm.js
CHANGED
|
@@ -415,7 +415,10 @@ const renderBuild = async (request, config, options) => {
|
|
|
415
415
|
for (;;) {
|
|
416
416
|
const status = await getRenderBuildStatus(job_id, config);
|
|
417
417
|
if (status.status === "completed") {
|
|
418
|
-
const
|
|
418
|
+
const requestedFormat = request.format ?? "video";
|
|
419
|
+
const finalUrl = (requestedFormat === "sprite"
|
|
420
|
+
? status.sprite_url || status.url || undefined
|
|
421
|
+
: status.video_url || status.url || undefined);
|
|
419
422
|
if (!finalUrl) {
|
|
420
423
|
throw new Error("Render job completed but no URL returned");
|
|
421
424
|
}
|
|
@@ -597,7 +600,7 @@ const useBuildRender = (parts, apiConfig, onLoadStart, options) => {
|
|
|
597
600
|
};
|
|
598
601
|
};
|
|
599
602
|
|
|
600
|
-
const useSpriteRender = (parts, apiConfig, onLoadStart) => {
|
|
603
|
+
const useSpriteRender = (parts, apiConfig, onLoadStart, options) => {
|
|
601
604
|
const [spriteSrc, setSpriteSrc] = useState(null);
|
|
602
605
|
const [isRenderingSprite, setIsRenderingSprite] = useState(false);
|
|
603
606
|
const [renderError, setRenderError] = useState(null);
|
|
@@ -608,21 +611,36 @@ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
|
|
|
608
611
|
setIsRenderingSprite(true);
|
|
609
612
|
setRenderError(null);
|
|
610
613
|
onLoadStart?.();
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
614
|
+
const mode = options?.mode ?? "async";
|
|
615
|
+
if (mode === "experimental") {
|
|
616
|
+
const response = await renderSpriteExperimental(currentParts, apiConfig);
|
|
617
|
+
const objectUrl = URL.createObjectURL(response.sprite);
|
|
618
|
+
// Clean up previous sprite URL before setting new one
|
|
619
|
+
setSpriteSrc((prevSrc) => {
|
|
620
|
+
if (prevSrc && prevSrc.startsWith("blob:")) {
|
|
621
|
+
URL.revokeObjectURL(prevSrc);
|
|
622
|
+
}
|
|
623
|
+
return objectUrl;
|
|
624
|
+
});
|
|
625
|
+
// Set sprite metadata
|
|
626
|
+
setSpriteMetadata({
|
|
627
|
+
cols: response.metadata?.cols || 12,
|
|
628
|
+
rows: response.metadata?.rows || 6,
|
|
629
|
+
totalFrames: response.metadata?.totalFrames || 72,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
// Async job-based flow: request sprite format and use returned URL
|
|
634
|
+
const { videoUrl: spriteUrl } = await renderBuild({ ...currentParts, format: "sprite" }, apiConfig);
|
|
635
|
+
setSpriteSrc((prevSrc) => {
|
|
636
|
+
if (prevSrc && prevSrc.startsWith("blob:")) {
|
|
637
|
+
URL.revokeObjectURL(prevSrc);
|
|
638
|
+
}
|
|
639
|
+
return spriteUrl;
|
|
640
|
+
});
|
|
641
|
+
// No metadata from async endpoint; keep defaults
|
|
642
|
+
setSpriteMetadata({ cols: 12, rows: 6, totalFrames: 72 });
|
|
643
|
+
}
|
|
626
644
|
}
|
|
627
645
|
catch (error) {
|
|
628
646
|
setRenderError(error instanceof Error ? error.message : "Failed to render sprite");
|
|
@@ -630,7 +648,7 @@ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
|
|
|
630
648
|
finally {
|
|
631
649
|
setIsRenderingSprite(false);
|
|
632
650
|
}
|
|
633
|
-
}, [apiConfig, onLoadStart]);
|
|
651
|
+
}, [apiConfig, onLoadStart, options?.mode]);
|
|
634
652
|
// Effect to call API when parts content changes (using custom equality check)
|
|
635
653
|
useEffect(() => {
|
|
636
654
|
const shouldFetch = previousPartsRef.current === null ||
|
|
@@ -643,7 +661,7 @@ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
|
|
|
643
661
|
// Cleanup effect for component unmount
|
|
644
662
|
useEffect(() => {
|
|
645
663
|
return () => {
|
|
646
|
-
if (spriteSrc) {
|
|
664
|
+
if (spriteSrc && spriteSrc.startsWith("blob:")) {
|
|
647
665
|
URL.revokeObjectURL(spriteSrc);
|
|
648
666
|
}
|
|
649
667
|
};
|
|
@@ -711,11 +729,13 @@ const InstructionTooltip = ({ isVisible, progressValue, instructionIcon, }) => {
|
|
|
711
729
|
} })) }));
|
|
712
730
|
};
|
|
713
731
|
|
|
714
|
-
const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSensitivity = 0.2, }) => {
|
|
732
|
+
const BuildRender = ({ parts, width, height, size, apiConfig, mouseSensitivity = 0.2, touchSensitivity = 0.2, }) => {
|
|
715
733
|
const canvasRef = useRef(null);
|
|
716
734
|
const [img, setImg] = useState(null);
|
|
717
735
|
const [isLoading, setIsLoading] = useState(true);
|
|
718
736
|
const [bouncingAllowed, setBouncingAllowed] = useState(false);
|
|
737
|
+
const displayW = width ?? size ?? 300;
|
|
738
|
+
const displayH = height ?? size ?? 300;
|
|
719
739
|
// Use custom hook for sprite rendering
|
|
720
740
|
const { spriteSrc, isRenderingSprite, renderError, spriteMetadata } = useSpriteRender(parts, apiConfig);
|
|
721
741
|
const { value: progressValue, isBouncing } = useBouncePatternProgress(bouncingAllowed);
|
|
@@ -761,8 +781,8 @@ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSens
|
|
|
761
781
|
return;
|
|
762
782
|
// Backing store sized for HiDPI; CSS size stays `size`
|
|
763
783
|
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
764
|
-
const targetW = Math.round(
|
|
765
|
-
const targetH = Math.round(
|
|
784
|
+
const targetW = Math.round(displayW * dpr);
|
|
785
|
+
const targetH = Math.round(displayH * dpr);
|
|
766
786
|
if (cnv.width !== targetW || cnv.height !== targetH) {
|
|
767
787
|
cnv.width = targetW;
|
|
768
788
|
cnv.height = targetH;
|
|
@@ -782,7 +802,7 @@ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSens
|
|
|
782
802
|
ctx.imageSmoothingEnabled = true;
|
|
783
803
|
ctx.imageSmoothingQuality = "high";
|
|
784
804
|
ctx.drawImage(img, sx, sy, sw, sh, 0, 0, targetW, targetH);
|
|
785
|
-
}, [img, frameW, frameH,
|
|
805
|
+
}, [img, frameW, frameH, displayW, displayH, cols, total]);
|
|
786
806
|
const { isDragging, handleMouseDown, handleTouchStart, hasDragged } = useSpriteScrubbing(canvasRef, total, {
|
|
787
807
|
mouseSensitivity,
|
|
788
808
|
touchSensitivity,
|
|
@@ -812,19 +832,19 @@ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSens
|
|
|
812
832
|
}, [img, isLoading, draw]);
|
|
813
833
|
return (jsxs("div", { style: {
|
|
814
834
|
position: "relative",
|
|
815
|
-
width:
|
|
816
|
-
height:
|
|
835
|
+
width: displayW,
|
|
836
|
+
height: displayH,
|
|
817
837
|
backgroundColor: "black",
|
|
818
838
|
}, children: [img && (jsx("canvas", { ref: canvasRef, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, style: {
|
|
819
|
-
width:
|
|
820
|
-
height:
|
|
839
|
+
width: displayW,
|
|
840
|
+
height: displayH,
|
|
821
841
|
cursor: isDragging ? "grabbing" : "grab",
|
|
822
842
|
touchAction: "none", // Prevents default touch behaviors like scrolling
|
|
823
843
|
display: "block",
|
|
824
844
|
userSelect: "none",
|
|
825
845
|
WebkitUserSelect: "none",
|
|
826
846
|
WebkitTouchCallout: "none",
|
|
827
|
-
}, role: "img", "aria-label": "360\u00B0 viewer", onContextMenu: (e) => e.preventDefault() })), jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingSprite || !!renderError, renderError: renderError || undefined, size:
|
|
847
|
+
}, role: "img", "aria-label": "360\u00B0 viewer", onContextMenu: (e) => e.preventDefault() })), jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingSprite || !!renderError, renderError: renderError || undefined, size: Math.min(displayW, displayH) }), jsx(InstructionTooltip, { isVisible: !isLoading &&
|
|
828
848
|
!isRenderingSprite &&
|
|
829
849
|
!renderError &&
|
|
830
850
|
isBouncing &&
|
|
@@ -924,10 +944,12 @@ const useVideoScrubbing = (videoRef, options = {}) => {
|
|
|
924
944
|
};
|
|
925
945
|
};
|
|
926
946
|
|
|
927
|
-
const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, touchSensitivity = 0.01, }) => {
|
|
947
|
+
const BuildRenderVideo = ({ parts, width, height, size, apiConfig, mouseSensitivity = 0.01, touchSensitivity = 0.01, }) => {
|
|
928
948
|
const videoRef = useRef(null);
|
|
929
949
|
const [isLoading, setIsLoading] = useState(true);
|
|
930
950
|
const [bouncingAllowed, setBouncingAllowed] = useState(false);
|
|
951
|
+
const displayW = width ?? size ?? 300;
|
|
952
|
+
const displayH = height ?? size ?? 300;
|
|
931
953
|
// Use custom hook for build rendering
|
|
932
954
|
const { videoSrc, isRenderingBuild, renderError } = useBuildRender(parts, apiConfig);
|
|
933
955
|
const { value: progressValue, isBouncing } = useBouncePatternProgress(bouncingAllowed);
|
|
@@ -957,7 +979,7 @@ const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, tou
|
|
|
957
979
|
videoRef.current.currentTime = time;
|
|
958
980
|
}
|
|
959
981
|
}, [progressValue, hasDragged]);
|
|
960
|
-
return (jsxs("div", { style: { position: "relative", width:
|
|
982
|
+
return (jsxs("div", { style: { position: "relative", width: displayW, height: displayH }, children: [videoSrc && (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: () => {
|
|
961
983
|
if (videoRef.current) {
|
|
962
984
|
videoRef.current.pause();
|
|
963
985
|
}
|
|
@@ -975,7 +997,7 @@ const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, tou
|
|
|
975
997
|
WebkitTouchCallout: "none",
|
|
976
998
|
WebkitUserSelect: "none",
|
|
977
999
|
userSelect: "none",
|
|
978
|
-
}, children: "Your browser does not support the video tag." }, videoSrc)), jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingBuild || !!renderError, renderError: renderError || undefined, size:
|
|
1000
|
+
}, children: "Your browser does not support the video tag." }, videoSrc)), jsx(LoadingErrorOverlay, { isVisible: isLoading || isRenderingBuild || !!renderError, renderError: renderError || undefined, size: Math.min(displayW, displayH) }), jsx(InstructionTooltip, { isVisible: !isLoading &&
|
|
979
1001
|
!isRenderingBuild &&
|
|
980
1002
|
!renderError &&
|
|
981
1003
|
isBouncing &&
|