NITISHRG15102007's picture
Finalize submission checks and long inference output format
fea495a verified
"use client";
// src/next/index.tsx
import { useRef as useRef2, useState as useState3, useEffect as useEffect3 } from "react";
import Script from "next/script";
import Image from "next/image";
// src/next/hooks.ts
import { useState as useState2, useCallback as useCallback2, useEffect as useEffect2 } from "react";
// src/shared/hooks.ts
import { useEffect, useRef, useState, useCallback, useMemo } from "react";
// src/shared/constants.ts
var UNICORN_STUDIO_VERSION = "2.1.4";
var UNICORN_STUDIO_CDN_URL = `https://cdn.jsdelivr.net/gh/hiunicornstudio/unicornstudio.js@v${UNICORN_STUDIO_VERSION}/dist/unicornStudio.umd.js`;
var DEFAULT_VALUES = {
/** Default container width */
width: "100%",
/** Default container height */
height: "100%",
/** Default rendering scale (0.25 to 1.0) */
scale: 1,
/** Default production mode setting */
production: true,
/** Default device pixel ratio */
dpi: 1.5,
/** Default frames per second */
fps: 60,
/** Default alt text for accessibility */
altText: "Scene",
/** Default CSS class name */
className: "",
/** Default paused state */
paused: false,
/** Default lazy loading setting */
lazyLoad: true,
/** Default setting for showing placeholder on error */
showPlaceholderOnError: true,
/** Default setting for showing placeholder while loading */
showPlaceholderWhileLoading: true
};
var VALID_FPS = [15, 24, 30, 60, 120];
// src/shared/utils.ts
function isWebGLSupported() {
if (typeof window === "undefined") return true;
try {
const canvas = document.createElement("canvas");
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
return !!gl;
} catch (e) {
return false;
}
}
function validateFPS(fps) {
return VALID_FPS.includes(fps);
}
function validateScale(scale) {
return scale >= 0.25 && scale <= 1;
}
function validateParameters(scale, fps) {
if (!validateScale(scale)) {
return `Invalid scale: ${scale}. Scale must be between 0.25 and 1.0`;
}
if (!validateFPS(fps)) {
return `Invalid fps: ${fps}. FPS must be one of: 15, 24, 30, 60, 120`;
}
return null;
}
// src/shared/hooks.ts
function sanitizeErrorMessage(message) {
if (message.includes("404") || message.includes("Failed to fetch")) {
return "Resource not found";
}
if (message.includes("Network") || message.includes("network")) {
return "Network error occurred";
}
if (message.includes("timeout")) {
return "Loading timeout";
}
const urlPattern = /\bhttps?:\/\/[^\s)]+/gi;
const filePathPattern = /\b(?:[A-Za-z]:\\|\/)[^\s)]+/g;
let sanitized = message.replaceAll(urlPattern, "[redacted]");
sanitized = sanitized.replaceAll(filePathPattern, "[redacted]");
return sanitized;
}
function buildSceneConfig(element, params) {
const elementId = element.id || `unicorn-${Math.random().toString(36).slice(2, 11)}`;
if (!element.id) {
element.id = elementId;
}
const config = {
elementId,
scale: params.scale,
dpi: params.dpi,
fps: params.fps,
lazyLoad: params.lazyLoad,
altText: params.altText,
ariaLabel: params.ariaLabel,
production: params.production
};
if (params.jsonFilePath) {
config.filePath = params.jsonFilePath;
} else if (params.projectId) {
config.projectId = params.projectId;
} else {
throw new Error("No project ID or JSON file path provided");
}
return config;
}
var INIT_TIMEOUT_MS = 15e3;
async function withTimeout(promise, ms = INIT_TIMEOUT_MS) {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(
() => reject(new Error("Scene initialization timeout")),
ms
);
});
try {
return await Promise.race([promise, timeoutPromise]);
} finally {
if (timeoutId) clearTimeout(timeoutId);
}
}
function assignSceneRef(ref, value) {
if (!ref) return;
if (typeof ref === "function") {
ref(value);
return;
}
ref.current = value;
}
function useUnicornScene({
elementRef,
projectId,
jsonFilePath,
production,
scale,
dpi,
fps,
lazyLoad,
altText,
ariaLabel,
isScriptLoaded,
paused,
onLoad,
onError,
sceneRef
}) {
const internalSceneRef = useRef(null);
const [initError, setInitError] = useState(null);
const initializationKeyRef = useRef("");
const isInitializingRef = useRef(false);
const onLoadRef = useRef(onLoad);
onLoadRef.current = onLoad;
const onErrorRef = useRef(onError);
onErrorRef.current = onError;
const sceneRefRef = useRef(sceneRef);
const prevSceneRef = useRef(sceneRef);
if (sceneRef !== prevSceneRef.current) {
assignSceneRef(prevSceneRef.current, null);
sceneRefRef.current = sceneRef;
prevSceneRef.current = sceneRef;
assignSceneRef(sceneRef, internalSceneRef.current);
}
const validationError = useMemo(() => {
return validateParameters(scale, fps);
}, [scale, fps]);
const prevValidationError = useRef(null);
useEffect(() => {
var _a;
if (validationError !== prevValidationError.current) {
prevValidationError.current = validationError;
if (validationError) {
const error = new Error(validationError);
setInitError(error);
(_a = onErrorRef.current) == null ? void 0 : _a.call(onErrorRef, error);
} else {
setInitError(null);
}
}
}, [validationError]);
const destroyScene = useCallback(() => {
var _a;
if ((_a = internalSceneRef.current) == null ? void 0 : _a.destroy) {
internalSceneRef.current.destroy();
internalSceneRef.current = null;
assignSceneRef(sceneRefRef.current, null);
}
isInitializingRef.current = false;
}, []);
useEffect(() => {
let ignore = false;
async function initializeScene() {
var _a, _b, _c;
if (!elementRef.current || !isScriptLoaded || validationError) return;
if (isInitializingRef.current) return;
const currentKey = `${projectId || ""}-${jsonFilePath || ""}-${scale}-${dpi}-${fps}-${production ? "prod" : "dev"}`;
if (initializationKeyRef.current === currentKey && internalSceneRef.current) {
return;
}
initializationKeyRef.current = currentKey;
try {
destroyScene();
isInitializingRef.current = true;
if (!((_a = window.UnicornStudio) == null ? void 0 : _a.addScene)) {
throw new Error("UnicornStudio.addScene not found");
}
const sceneConfig = buildSceneConfig(elementRef.current, {
jsonFilePath,
projectId,
scale,
dpi,
fps,
lazyLoad,
altText,
ariaLabel,
production
});
const scene = await withTimeout(
window.UnicornStudio.addScene(sceneConfig)
);
if (ignore) {
scene == null ? void 0 : scene.destroy();
return;
}
if (scene) {
internalSceneRef.current = scene;
assignSceneRef(sceneRefRef.current, scene);
setInitError(null);
isInitializingRef.current = false;
(_b = onLoadRef.current) == null ? void 0 : _b.call(onLoadRef);
} else {
isInitializingRef.current = false;
throw new Error("Failed to initialize scene");
}
} catch (error) {
if (ignore) return;
const err = error instanceof Error ? error : new Error("Unknown error");
const sanitizedError = new Error(sanitizeErrorMessage(err.message));
setInitError(sanitizedError);
isInitializingRef.current = false;
(_c = onErrorRef.current) == null ? void 0 : _c.call(onErrorRef, sanitizedError);
}
}
if (isScriptLoaded) {
void initializeScene();
}
return () => {
ignore = true;
destroyScene();
};
}, [
isScriptLoaded,
elementRef,
jsonFilePath,
projectId,
production,
scale,
dpi,
fps,
lazyLoad,
altText,
ariaLabel,
destroyScene,
validationError
]);
useEffect(() => {
if (internalSceneRef.current && paused !== void 0) {
internalSceneRef.current.paused = paused;
}
}, [paused]);
useEffect(() => {
const el = elementRef.current;
if (!el || typeof ResizeObserver === "undefined") return;
const observer = new ResizeObserver(() => {
var _a, _b;
(_b = (_a = internalSceneRef.current) == null ? void 0 : _a.resize) == null ? void 0 : _b.call(_a);
});
observer.observe(el);
return () => {
observer.disconnect();
};
}, [elementRef]);
return { error: initError };
}
// src/next/hooks.ts
function useUnicornStudioScript() {
const [isLoaded, setIsLoaded] = useState2(false);
const [error, setError] = useState2(null);
const handleScriptLoad = useCallback2(() => {
setIsLoaded((prev) => {
if (!prev && typeof window !== "undefined" && window.UnicornStudio) {
setError(null);
return true;
}
return prev;
});
}, []);
const handleScriptError = useCallback2(() => {
setError(new Error("Failed to load UnicornStudio script"));
setIsLoaded(false);
}, []);
useEffect2(() => {
if (typeof window !== "undefined" && window.UnicornStudio && !isLoaded) {
setIsLoaded(true);
setError(null);
}
}, [isLoaded]);
return { isLoaded, error, handleScriptLoad, handleScriptError };
}
// src/shared/styles.ts
var unicornStyles = {
/**
* Styles for the main scene container element.
*
* @remarks
* Uses CSS custom properties for width and height to allow dynamic sizing.
*/
container: {
position: "relative",
width: "var(--unicorn-width)",
height: "var(--unicorn-height)"
},
/**
* Styles for the error message wrapper.
*/
errorWrapper: {
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100%"
},
/**
* Styles for the error message box.
*/
errorBox: {
textAlign: "center",
padding: "1rem",
borderRadius: "0.5rem",
backgroundColor: "rgb(254 242 242)",
color: "rgb(239 68 68)"
},
/**
* Styles for the error title text.
*/
errorTitle: {
fontWeight: "600",
marginBottom: "0.25rem"
},
/**
* Styles for the error message text.
*/
errorMessage: {
fontSize: "0.875rem",
marginTop: "0.25rem"
}
};
// src/next/index.tsx
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
function UnicornScene({
projectId,
jsonFilePath,
sdkUrl = UNICORN_STUDIO_CDN_URL,
width = DEFAULT_VALUES.width,
height = DEFAULT_VALUES.height,
scale = DEFAULT_VALUES.scale,
dpi = DEFAULT_VALUES.dpi,
fps = DEFAULT_VALUES.fps,
altText = DEFAULT_VALUES.altText,
ariaLabel,
className = DEFAULT_VALUES.className,
lazyLoad = DEFAULT_VALUES.lazyLoad,
production = DEFAULT_VALUES.production,
paused = DEFAULT_VALUES.paused,
placeholder,
placeholderClassName,
showPlaceholderOnError = DEFAULT_VALUES.showPlaceholderOnError,
showPlaceholderWhileLoading = DEFAULT_VALUES.showPlaceholderWhileLoading,
onLoad,
onError,
sceneRef
}) {
const elementRef = useRef2(null);
const [isSceneLoaded, setIsSceneLoaded] = useState3(false);
const [webGLSupported, setWebGLSupported] = useState3(true);
const {
isLoaded,
error: scriptError,
handleScriptLoad,
handleScriptError
} = useUnicornStudioScript();
const { error: sceneError } = useUnicornScene({
elementRef,
projectId,
jsonFilePath,
production,
scale,
dpi,
fps,
lazyLoad,
altText,
ariaLabel: ariaLabel || altText,
isScriptLoaded: isLoaded,
paused,
sceneRef,
onLoad: () => {
setIsSceneLoaded(true);
onLoad == null ? void 0 : onLoad();
},
onError
});
const error = scriptError || sceneError;
useEffect3(() => {
setWebGLSupported(isWebGLSupported());
}, []);
const showPlaceholder = (placeholder || placeholderClassName) && (!webGLSupported || showPlaceholderWhileLoading && !isSceneLoaded || showPlaceholderOnError && error);
const numericWidth = typeof width === "number" ? width : 0;
const numericHeight = typeof height === "number" ? height : 0;
const useNumericDimensions = typeof width === "number" && typeof height === "number";
const customProperties = {
"--unicorn-width": typeof width === "number" ? `${width}px` : width,
"--unicorn-height": typeof height === "number" ? `${height}px` : height
};
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
Script,
{
src: sdkUrl,
strategy: lazyLoad ? "lazyOnload" : "afterInteractive",
onLoad: handleScriptLoad,
onError: handleScriptError
}
),
/* @__PURE__ */ jsxs(
"div",
{
ref: elementRef,
style: { ...unicornStyles.container, ...customProperties },
className,
children: [
showPlaceholder && (placeholder || placeholderClassName) && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0 }, children: typeof placeholder === "string" ? useNumericDimensions ? /* @__PURE__ */ jsx(
Image,
{
src: placeholder,
alt: altText,
width: numericWidth,
height: numericHeight,
style: { objectFit: "cover" },
priority: true
}
) : /* @__PURE__ */ jsx(
Image,
{
src: placeholder,
alt: altText,
fill: true,
style: { objectFit: "cover" },
priority: true
}
) : placeholder ? placeholder : placeholderClassName ? /* @__PURE__ */ jsx(
"div",
{
className: placeholderClassName,
style: { width: "100%", height: "100%" },
"aria-label": altText
}
) : null }),
error && !showPlaceholder && /* @__PURE__ */ jsx("div", { style: unicornStyles.errorWrapper, children: /* @__PURE__ */ jsxs("div", { style: unicornStyles.errorBox, children: [
/* @__PURE__ */ jsx("p", { style: unicornStyles.errorTitle, children: "Error loading scene" }),
/* @__PURE__ */ jsx("p", { style: unicornStyles.errorMessage, children: error.message })
] }) })
]
}
)
] });
}
var next_default = UnicornScene;
export {
UnicornScene,
next_default as default
};
//# sourceMappingURL=next.mjs.map