@buildcores/render-client 1.0.5 → 1.0.7

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
@@ -322,27 +322,41 @@ function useBouncePatternProgress(enabled = true) {
322
322
  }
323
323
 
324
324
  // API Configuration
325
- const API_BASE_URL = "https://squid-app-7aeyk.ondigitalocean.app";
325
+ const API_BASE_URL = "https://www.renderapi.buildcores.com";
326
326
  // API Endpoints
327
327
  const API_ENDPOINTS = {
328
328
  RENDER_BUILD_EXPERIMENTAL: "/render-build-experimental",
329
329
  AVAILABLE_PARTS: "/available-parts",
330
330
  };
331
331
  // API URL helpers
332
- const buildApiUrl = (endpoint) => {
333
- return `${API_BASE_URL}${endpoint}`;
332
+ const buildApiUrl = (endpoint, config) => {
333
+ const baseUrl = `${API_BASE_URL}${endpoint}`;
334
+ if (config.environment) {
335
+ const separator = endpoint.includes("?") ? "&" : "?";
336
+ return `${baseUrl}${separator}environment=${config.environment}`;
337
+ }
338
+ return baseUrl;
339
+ };
340
+ // Helper to build request headers with auth token
341
+ const buildHeaders = (config) => {
342
+ const headers = {
343
+ "Content-Type": "application/json",
344
+ accept: "application/json",
345
+ };
346
+ if (config.authToken) {
347
+ headers["Authorization"] = `Bearer ${config.authToken}`;
348
+ }
349
+ return headers;
334
350
  };
335
351
  // API Implementation
336
- const renderBuildExperimental = async (request) => {
352
+ const renderBuildExperimental = async (request, config) => {
337
353
  const requestWithFormat = {
338
354
  ...request,
339
355
  format: request.format || "video", // Default to video format
340
356
  };
341
- const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL), {
357
+ const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL, config), {
342
358
  method: "POST",
343
- headers: {
344
- "Content-Type": "application/json",
345
- },
359
+ headers: buildHeaders(config),
346
360
  body: JSON.stringify(requestWithFormat),
347
361
  });
348
362
  if (!response.ok) {
@@ -357,16 +371,14 @@ const renderBuildExperimental = async (request) => {
357
371
  },
358
372
  };
359
373
  };
360
- const renderSpriteExperimental = async (request) => {
374
+ const renderSpriteExperimental = async (request, config) => {
361
375
  const requestWithFormat = {
362
376
  ...request,
363
377
  format: "sprite",
364
378
  };
365
- const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL), {
379
+ const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD_EXPERIMENTAL, config), {
366
380
  method: "POST",
367
- headers: {
368
- "Content-Type": "application/json",
369
- },
381
+ headers: buildHeaders(config),
370
382
  body: JSON.stringify(requestWithFormat),
371
383
  });
372
384
  if (!response.ok) {
@@ -384,12 +396,10 @@ const renderSpriteExperimental = async (request) => {
384
396
  },
385
397
  };
386
398
  };
387
- const getAvailableParts = async () => {
388
- const response = await fetch(buildApiUrl(API_ENDPOINTS.AVAILABLE_PARTS), {
399
+ const getAvailableParts = async (config) => {
400
+ const response = await fetch(buildApiUrl(API_ENDPOINTS.AVAILABLE_PARTS, config), {
389
401
  method: "GET",
390
- headers: {
391
- "Content-Type": "application/json",
392
- },
402
+ headers: buildHeaders(config),
393
403
  });
394
404
  if (!response.ok) {
395
405
  throw new Error(`Get available parts failed: ${response.status} ${response.statusText}`);
@@ -397,7 +407,6 @@ const getAvailableParts = async () => {
397
407
  return response.json();
398
408
  };
399
409
 
400
- // API Types
401
410
  /**
402
411
  * Enum defining all available PC part categories that can be rendered.
403
412
  *
@@ -466,7 +475,7 @@ const arePartsEqual = (parts1, parts2) => {
466
475
  }
467
476
  return true;
468
477
  };
469
- const useBuildRender = (parts, onLoadStart) => {
478
+ const useBuildRender = (parts, apiConfig, onLoadStart) => {
470
479
  const [videoSrc, setVideoSrc] = React.useState(null);
471
480
  const [isRenderingBuild, setIsRenderingBuild] = React.useState(false);
472
481
  const [renderError, setRenderError] = React.useState(null);
@@ -476,7 +485,7 @@ const useBuildRender = (parts, onLoadStart) => {
476
485
  setIsRenderingBuild(true);
477
486
  setRenderError(null);
478
487
  onLoadStart?.();
479
- const response = await renderBuildExperimental(currentParts);
488
+ const response = await renderBuildExperimental(currentParts, apiConfig);
480
489
  const objectUrl = URL.createObjectURL(response.video);
481
490
  // Clean up previous video URL before setting new one
482
491
  setVideoSrc((prevSrc) => {
@@ -492,7 +501,7 @@ const useBuildRender = (parts, onLoadStart) => {
492
501
  finally {
493
502
  setIsRenderingBuild(false);
494
503
  }
495
- }, [onLoadStart]);
504
+ }, [apiConfig, onLoadStart]);
496
505
  // Effect to call API when parts content changes (using custom equality check)
497
506
  React.useEffect(() => {
498
507
  const shouldFetch = previousPartsRef.current === null ||
@@ -517,7 +526,7 @@ const useBuildRender = (parts, onLoadStart) => {
517
526
  };
518
527
  };
519
528
 
520
- const useSpriteRender = (parts, onLoadStart) => {
529
+ const useSpriteRender = (parts, apiConfig, onLoadStart) => {
521
530
  const [spriteSrc, setSpriteSrc] = React.useState(null);
522
531
  const [isRenderingSprite, setIsRenderingSprite] = React.useState(false);
523
532
  const [renderError, setRenderError] = React.useState(null);
@@ -528,7 +537,7 @@ const useSpriteRender = (parts, onLoadStart) => {
528
537
  setIsRenderingSprite(true);
529
538
  setRenderError(null);
530
539
  onLoadStart?.();
531
- const response = await renderSpriteExperimental(currentParts);
540
+ const response = await renderSpriteExperimental(currentParts, apiConfig);
532
541
  const objectUrl = URL.createObjectURL(response.sprite);
533
542
  // Clean up previous sprite URL before setting new one
534
543
  setSpriteSrc((prevSrc) => {
@@ -550,7 +559,7 @@ const useSpriteRender = (parts, onLoadStart) => {
550
559
  finally {
551
560
  setIsRenderingSprite(false);
552
561
  }
553
- }, [onLoadStart]);
562
+ }, [apiConfig, onLoadStart]);
554
563
  // Effect to call API when parts content changes (using custom equality check)
555
564
  React.useEffect(() => {
556
565
  const shouldFetch = previousPartsRef.current === null ||
@@ -631,13 +640,13 @@ const InstructionTooltip = ({ isVisible, progressValue, instructionIcon, }) => {
631
640
  } })) }));
632
641
  };
633
642
 
634
- const BuildRender = ({ parts, size, mouseSensitivity = 0.2, touchSensitivity = 0.2, }) => {
643
+ const BuildRender = ({ parts, size, apiConfig, mouseSensitivity = 0.2, touchSensitivity = 0.2, }) => {
635
644
  const canvasRef = React.useRef(null);
636
645
  const [img, setImg] = React.useState(null);
637
646
  const [isLoading, setIsLoading] = React.useState(true);
638
647
  const [bouncingAllowed, setBouncingAllowed] = React.useState(false);
639
648
  // Use custom hook for sprite rendering
640
- const { spriteSrc, isRenderingSprite, renderError, spriteMetadata } = useSpriteRender(parts);
649
+ const { spriteSrc, isRenderingSprite, renderError, spriteMetadata } = useSpriteRender(parts, apiConfig);
641
650
  const { value: progressValue, isBouncing } = useBouncePatternProgress(bouncingAllowed);
642
651
  const total = spriteMetadata ? spriteMetadata.totalFrames : 72;
643
652
  const cols = spriteMetadata ? spriteMetadata.cols : 12;
@@ -844,14 +853,74 @@ const useVideoScrubbing = (videoRef, options = {}) => {
844
853
  };
845
854
  };
846
855
 
856
+ const BuildRenderVideo = ({ parts, size, apiConfig, mouseSensitivity = 0.01, touchSensitivity = 0.01, }) => {
857
+ const videoRef = React.useRef(null);
858
+ const [isLoading, setIsLoading] = React.useState(true);
859
+ const [bouncingAllowed, setBouncingAllowed] = React.useState(false);
860
+ // Use custom hook for build rendering
861
+ const { videoSrc, isRenderingBuild, renderError } = useBuildRender(parts, apiConfig);
862
+ const { value: progressValue, isBouncing } = useBouncePatternProgress(bouncingAllowed);
863
+ const { isDragging, handleMouseDown, handleTouchStart, hasDragged } = useVideoScrubbing(videoRef, {
864
+ mouseSensitivity,
865
+ touchSensitivity,
866
+ });
867
+ const handleLoadStartInternal = React.useCallback(() => {
868
+ setIsLoading(true);
869
+ setBouncingAllowed(false);
870
+ }, []);
871
+ const handleCanPlayInternal = React.useCallback(() => {
872
+ setIsLoading(false);
873
+ // Start bouncing animation after delay
874
+ setTimeout(() => {
875
+ setBouncingAllowed(true);
876
+ }, 2000);
877
+ }, []);
878
+ React.useEffect(() => {
879
+ if (hasDragged.current || !videoRef.current)
880
+ return;
881
+ const duration = videoRef.current.duration;
882
+ if (!isFinite(duration))
883
+ return;
884
+ const time = calculateCircularTime(0, progressValue, 0.5, duration);
885
+ if (isFinite(time)) {
886
+ videoRef.current.currentTime = time;
887
+ }
888
+ }, [progressValue, hasDragged]);
889
+ 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: () => {
890
+ if (videoRef.current) {
891
+ videoRef.current.pause();
892
+ }
893
+ }, style: {
894
+ cursor: isDragging ? "grabbing" : "grab",
895
+ touchAction: "none", // Prevents default touch behaviors like scrolling
896
+ display: "block",
897
+ // Completely hide video controls on all browsers including mobile
898
+ WebkitMediaControls: "none",
899
+ MozMediaControls: "none",
900
+ OMediaControls: "none",
901
+ msMediaControls: "none",
902
+ mediaControls: "none",
903
+ // Additional iOS-specific properties
904
+ WebkitTouchCallout: "none",
905
+ WebkitUserSelect: "none",
906
+ userSelect: "none",
907
+ }, 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 &&
908
+ !isRenderingBuild &&
909
+ !renderError &&
910
+ isBouncing &&
911
+ !hasDragged.current, progressValue: progressValue })] }));
912
+ };
913
+
847
914
  exports.API_BASE_URL = API_BASE_URL;
848
915
  exports.API_ENDPOINTS = API_ENDPOINTS;
849
916
  exports.BuildRender = BuildRender;
917
+ exports.BuildRenderVideo = BuildRenderVideo;
850
918
  exports.DragIcon = DragIcon;
851
919
  exports.InstructionTooltip = InstructionTooltip;
852
920
  exports.LoadingErrorOverlay = LoadingErrorOverlay;
853
921
  exports.arePartsEqual = arePartsEqual;
854
922
  exports.buildApiUrl = buildApiUrl;
923
+ exports.buildHeaders = buildHeaders;
855
924
  exports.calculateCircularFrame = calculateCircularFrame;
856
925
  exports.calculateCircularTime = calculateCircularTime;
857
926
  exports.getAvailableParts = getAvailableParts;