@cimplify/sdk 0.48.2 → 0.49.1
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/react.d.mts +79 -1
- package/dist/react.d.ts +79 -1
- package/dist/react.js +288 -2
- package/dist/react.mjs +286 -3
- package/dist/server.d.mts +74 -10
- package/dist/server.d.ts +74 -10
- package/dist/server.js +262 -17
- package/dist/server.mjs +235 -18
- package/package.json +1 -1
- package/registry/index.json +24 -0
- package/registry/media-gallery.json +16 -0
- package/registry/product-model-3d.json +13 -0
- package/registry/store-video.json +13 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "product-model-3d",
|
|
3
|
+
"title": "ProductModel3D",
|
|
4
|
+
"description": "Interactive 3D product viewer wrapping Google's <model-viewer>. Loads the web component on demand; AR mode lights up on iOS (Quick Look) and Android (Scene Viewer) automatically.",
|
|
5
|
+
"type": "component",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"files": [
|
|
8
|
+
{
|
|
9
|
+
"path": "product-model-3d.tsx",
|
|
10
|
+
"content": "\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\n\nexport interface ProductModel3DProps {\n /** glTF or GLB URL. */\n src: string;\n /** Optional USDZ URL for iOS AR Quick Look (Safari/WebKit). */\n iosSrc?: string;\n /** Poster image shown while the model loads (and as a fallback). */\n poster?: string;\n alt?: string;\n aspectRatio?: \"square\" | \"4/3\" | \"16/9\" | \"3/4\";\n /** Enable AR mode. Lights up on iOS (Quick Look) + Android (Scene Viewer). Default `true`. */\n ar?: boolean;\n autoRotate?: boolean;\n cameraControls?: boolean;\n className?: string;\n}\n\nconst ASPECT_STYLES: Record<string, React.CSSProperties> = {\n square: { aspectRatio: \"1/1\" },\n \"4/3\": { aspectRatio: \"4/3\" },\n \"16/9\": { aspectRatio: \"16/9\" },\n \"3/4\": { aspectRatio: \"3/4\" },\n};\n\nconst MODEL_VIEWER_CDN =\n \"https://unpkg.com/@google/model-viewer@4.0.0/dist/model-viewer.min.js\";\n\n// Module-level cache so multiple <ProductModel3D> instances share one script load.\nlet modelViewerLoadPromise: Promise<void> | null = null;\nfunction ensureModelViewer(): Promise<void> {\n if (typeof window === \"undefined\") return Promise.resolve();\n if (modelViewerLoadPromise) return modelViewerLoadPromise;\n if (window.customElements?.get(\"model-viewer\")) {\n modelViewerLoadPromise = Promise.resolve();\n return modelViewerLoadPromise;\n }\n modelViewerLoadPromise = new Promise<void>((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.type = \"module\";\n script.src = MODEL_VIEWER_CDN;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(\"Failed to load model-viewer\"));\n document.head.appendChild(script);\n });\n return modelViewerLoadPromise;\n}\n\n/**\n * ProductModel3D — interactive 3D product viewer wrapping Google's\n * `<model-viewer>` web component. Script loads on demand (one tag per page,\n * cached). AR mode auto-resolves the right OS surface: USDZ Quick Look on iOS,\n * Scene Viewer on Android, WebXR where available. Poster image shows during\n * load and as a fallback if the script can't load.\n */\nexport function ProductModel3D({\n src,\n iosSrc,\n poster,\n alt,\n aspectRatio = \"square\",\n ar = true,\n autoRotate = true,\n cameraControls = true,\n className,\n}: ProductModel3DProps): React.ReactElement {\n const [ready, setReady] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n ensureModelViewer().then(\n () => {\n if (!cancelled) setReady(true);\n },\n () => {\n // Stay on the poster fallback if the CDN script fails.\n },\n );\n return () => {\n cancelled = true;\n };\n }, []);\n\n return (\n <div\n data-cimplify-product-model-3d\n className={className}\n style={{ position: \"relative\", overflow: \"hidden\", ...ASPECT_STYLES[aspectRatio] }}\n >\n {ready\n ? React.createElement(\"model-viewer\", {\n src,\n \"ios-src\": iosSrc,\n alt,\n poster,\n ar: ar || undefined,\n \"ar-modes\": ar ? \"webxr scene-viewer quick-look\" : undefined,\n \"camera-controls\": cameraControls || undefined,\n \"auto-rotate\": autoRotate || undefined,\n \"shadow-intensity\": \"1\",\n style: { width: \"100%\", height: \"100%\", backgroundColor: \"transparent\" },\n })\n : poster\n ? React.createElement(\"img\", {\n src: poster,\n alt: alt ?? \"\",\n \"data-cimplify-product-model-3d-loading\": true,\n style: { width: \"100%\", height: \"100%\", objectFit: \"cover\" },\n })\n : React.createElement(\"div\", {\n \"data-cimplify-product-model-3d-loading\": true,\n style: { width: \"100%\", height: \"100%\", backgroundColor: \"var(--muted, #f3f4f6)\" },\n })}\n </div>\n );\n}\n"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "store-video",
|
|
3
|
+
"title": "StoreVideo",
|
|
4
|
+
"description": "Autoplay-muted-loop video for storefront heroes / product demos. Lazy-loaded, mobile-safe (playsInline), poster fallback for browsers without video.",
|
|
5
|
+
"type": "component",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"files": [
|
|
8
|
+
{
|
|
9
|
+
"path": "store-video.tsx",
|
|
10
|
+
"content": "\"use client\";\n\nimport React, { useEffect, useRef, useState } from \"react\";\n\nexport interface StoreVideoProps {\n src: string;\n /** Poster shown before play + as the fallback if `<video>` isn't supported. */\n poster?: string;\n /** Accessible label; also the alt of the fallback `<img>`. */\n alt?: string;\n aspectRatio?: \"square\" | \"4/3\" | \"16/9\" | \"16/10\" | \"3/4\";\n /** Show native browser controls. Default off for autoplay-friendly hero clips. */\n controls?: boolean;\n /** Default `true` — muted is the only way mobile browsers allow autoplay. */\n autoplay?: boolean;\n loop?: boolean;\n muted?: boolean;\n /** Lazy-load: defer the src until the player scrolls into view. Default `true`. */\n lazy?: boolean;\n className?: string;\n}\n\nconst ASPECT_STYLES: Record<string, React.CSSProperties> = {\n square: { aspectRatio: \"1/1\" },\n \"4/3\": { aspectRatio: \"4/3\" },\n \"16/9\": { aspectRatio: \"16/9\" },\n \"16/10\": { aspectRatio: \"16/10\" },\n \"3/4\": { aspectRatio: \"3/4\" },\n};\n\n/**\n * StoreVideo — `<video>` with mobile-safe defaults (muted + playsInline + loop),\n * IntersectionObserver-driven lazy load, and a poster `<img>` fallback for\n * browsers without video support. Drop-in replacement for a static hero image.\n */\nexport function StoreVideo({\n src,\n poster,\n alt,\n aspectRatio = \"16/9\",\n controls = false,\n autoplay = true,\n loop = true,\n muted = true,\n lazy = true,\n className,\n}: StoreVideoProps): React.ReactElement {\n const ref = useRef<HTMLVideoElement>(null);\n const [inView, setInView] = useState(!lazy);\n\n useEffect(() => {\n if (!lazy || inView) return;\n const node = ref.current;\n if (!node || typeof IntersectionObserver === \"undefined\") {\n setInView(true);\n return;\n }\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((e) => e.isIntersecting)) {\n setInView(true);\n observer.disconnect();\n }\n },\n { rootMargin: \"200px\" },\n );\n observer.observe(node);\n return () => observer.disconnect();\n }, [lazy, inView]);\n\n return (\n <div\n data-cimplify-store-video\n className={className}\n style={{ position: \"relative\", overflow: \"hidden\", ...ASPECT_STYLES[aspectRatio] }}\n >\n <video\n ref={ref}\n src={inView ? src : undefined}\n poster={poster}\n autoPlay={autoplay}\n loop={loop}\n muted={muted}\n playsInline\n controls={controls}\n preload={lazy ? \"metadata\" : \"auto\"}\n aria-label={alt}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n >\n {poster ? (\n <img\n src={poster}\n alt={alt ?? \"\"}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\" }}\n />\n ) : null}\n </video>\n </div>\n );\n}\n"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|