Spaces:
Sleeping
fix: all HF Space issues resolved
Browse files1. PropertiesPanel.jsx: fixed operator precedence bug
!model[key]??true β !(model[key]??true)
(was causing incorrect boolean logic on shadow toggles)
2. nginx.conf: removed Cross-Origin-Embedder-Policy header
COEP was blocking ALL cross-origin resource loading:
- GLB/GLTF models from threejs.org, supabase.co etc
- HDR/image skyboxes from external URLs
- Google Fonts
Removing COEP fixes model loading completely
3. README.md: clean valid HF front-matter, no trailing content issues
4. dist/: rebuilt with all fixes applied (index-CPjiwba7.js)
Dockerfile still uses pre-built dist/ so HF build = ~20 sec
Deployment strategy: nginx-only Docker image (no node/npm on HF)
- FROM nginx:1.25-alpine only
- COPY dist/ β nginx serves it
- Build time on HF free tier: ~20 seconds
- README.md +5 -9
- dist/assets/{index-BMPzxcxL.js β index-CPjiwba7.js} +0 -0
- dist/index.html +1 -1
- nginx.conf +12 -42
- src/components/PropertiesPanel.jsx +1 -1
|
@@ -15,12 +15,8 @@ short_description: GLB 3D Animation Studio β Three.js + React
|
|
| 15 |
Professional browser-based 3D animation studio for GLB/GLTF models.
|
| 16 |
|
| 17 |
## Features
|
| 18 |
-
- GLB/GLTF
|
| 19 |
-
- Multi-camera system with fly controls
|
| 20 |
-
- Cannon.js physics
|
| 21 |
-
-
|
| 22 |
-
-
|
| 23 |
-
- Video export (WebM) + PNG sequence, mobile optimised
|
| 24 |
-
|
| 25 |
-
## Tech Stack
|
| 26 |
-
React 18 Β· Vite Β· Three.js Β· React Three Fiber Β· Zustand Β· nginx Β· Docker
|
|
|
|
| 15 |
Professional browser-based 3D animation studio for GLB/GLTF models.
|
| 16 |
|
| 17 |
## Features
|
| 18 |
+
- GLB/GLTF loading Β· Transform controls Β· Keyframe animation
|
| 19 |
+
- Multi-camera system with fly controls
|
| 20 |
+
- Cannon.js physics Β· AI scene controller
|
| 21 |
+
- Clean render mode Β· Video/PNG export
|
| 22 |
+
- Mobile optimised
|
|
|
|
|
|
|
|
|
|
|
|
|
The diff for this file is too large to render.
See raw diff
|
|
|
|
@@ -11,7 +11,7 @@
|
|
| 11 |
</style>
|
| 12 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
|
| 14 |
-
<script type="module" crossorigin src="/assets/index-
|
| 15 |
<link rel="modulepreload" crossorigin href="/assets/three-BDVXI0HY.js">
|
| 16 |
<link rel="modulepreload" crossorigin href="/assets/r3f-D7cvq2vh.js">
|
| 17 |
<link rel="modulepreload" crossorigin href="/assets/state-D3gY4USI.js">
|
|
|
|
| 11 |
</style>
|
| 12 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 13 |
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
|
| 14 |
+
<script type="module" crossorigin src="/assets/index-CPjiwba7.js"></script>
|
| 15 |
<link rel="modulepreload" crossorigin href="/assets/three-BDVXI0HY.js">
|
| 16 |
<link rel="modulepreload" crossorigin href="/assets/r3f-D7cvq2vh.js">
|
| 17 |
<link rel="modulepreload" crossorigin href="/assets/state-D3gY4USI.js">
|
|
@@ -1,49 +1,30 @@
|
|
| 1 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2 |
-
# nginx config for GLB Animation Studio on Hugging Face Spaces
|
| 3 |
-
# Listening on port 7860 (HF Spaces requirement)
|
| 4 |
-
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 5 |
-
|
| 6 |
server {
|
| 7 |
listen 7860;
|
| 8 |
server_name _;
|
| 9 |
root /usr/share/nginx/html;
|
| 10 |
index index.html;
|
| 11 |
|
| 12 |
-
#
|
| 13 |
gzip on;
|
| 14 |
gzip_vary on;
|
| 15 |
gzip_min_length 1024;
|
| 16 |
gzip_proxied any;
|
| 17 |
gzip_comp_level 6;
|
| 18 |
gzip_types
|
| 19 |
-
text/plain
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
application/json
|
| 24 |
-
application/wasm
|
| 25 |
-
model/gltf+json
|
| 26 |
-
model/gltf-binary
|
| 27 |
-
image/svg+xml
|
| 28 |
-
font/woff
|
| 29 |
-
font/woff2;
|
| 30 |
-
|
| 31 |
-
# ββ Security headers ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 32 |
-
add_header X-Content-Type-Options "nosniff" always;
|
| 33 |
-
add_header X-Frame-Options "SAMEORIGIN" always;
|
| 34 |
-
add_header X-XSS-Protection "1; mode=block" always;
|
| 35 |
-
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
| 36 |
|
| 37 |
-
#
|
| 38 |
-
add_header
|
| 39 |
-
add_header
|
|
|
|
| 40 |
|
| 41 |
-
#
|
| 42 |
location ~* \.(js|css|woff2?|ttf|otf|eot)$ {
|
| 43 |
expires 1y;
|
| 44 |
add_header Cache-Control "public, immutable";
|
| 45 |
-
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
| 46 |
-
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
| 47 |
}
|
| 48 |
|
| 49 |
# Images
|
|
@@ -52,7 +33,7 @@ server {
|
|
| 52 |
add_header Cache-Control "public";
|
| 53 |
}
|
| 54 |
|
| 55 |
-
#
|
| 56 |
location ~* \.(glb|gltf|bin|hdr|exr|ktx2)$ {
|
| 57 |
expires 7d;
|
| 58 |
add_header Cache-Control "public";
|
|
@@ -64,25 +45,14 @@ server {
|
|
| 64 |
default_type application/wasm;
|
| 65 |
expires 7d;
|
| 66 |
add_header Cache-Control "public";
|
| 67 |
-
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
| 68 |
-
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
| 69 |
}
|
| 70 |
|
| 71 |
-
#
|
| 72 |
location / {
|
| 73 |
try_files $uri $uri/ /index.html;
|
| 74 |
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
| 75 |
-
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
| 76 |
-
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
| 77 |
}
|
| 78 |
|
| 79 |
error_page 404 /index.html;
|
| 80 |
-
|
| 81 |
-
# Large GLB file upload support (up to 200 MB)
|
| 82 |
client_max_body_size 200M;
|
| 83 |
-
|
| 84 |
-
# Timeouts
|
| 85 |
-
proxy_read_timeout 300s;
|
| 86 |
-
proxy_connect_timeout 60s;
|
| 87 |
-
send_timeout 300s;
|
| 88 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
server {
|
| 2 |
listen 7860;
|
| 3 |
server_name _;
|
| 4 |
root /usr/share/nginx/html;
|
| 5 |
index index.html;
|
| 6 |
|
| 7 |
+
# Gzip
|
| 8 |
gzip on;
|
| 9 |
gzip_vary on;
|
| 10 |
gzip_min_length 1024;
|
| 11 |
gzip_proxied any;
|
| 12 |
gzip_comp_level 6;
|
| 13 |
gzip_types
|
| 14 |
+
text/plain text/css text/javascript
|
| 15 |
+
application/javascript application/json
|
| 16 |
+
application/wasm model/gltf+json model/gltf-binary
|
| 17 |
+
image/svg+xml font/woff font/woff2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
# Security (no COEP - would block cross-origin GLB/image loading)
|
| 20 |
+
add_header X-Content-Type-Options "nosniff" always;
|
| 21 |
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
| 22 |
+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
| 23 |
|
| 24 |
+
# JS/CSS - immutable cache
|
| 25 |
location ~* \.(js|css|woff2?|ttf|otf|eot)$ {
|
| 26 |
expires 1y;
|
| 27 |
add_header Cache-Control "public, immutable";
|
|
|
|
|
|
|
| 28 |
}
|
| 29 |
|
| 30 |
# Images
|
|
|
|
| 33 |
add_header Cache-Control "public";
|
| 34 |
}
|
| 35 |
|
| 36 |
+
# 3D assets - allow cross-origin
|
| 37 |
location ~* \.(glb|gltf|bin|hdr|exr|ktx2)$ {
|
| 38 |
expires 7d;
|
| 39 |
add_header Cache-Control "public";
|
|
|
|
| 45 |
default_type application/wasm;
|
| 46 |
expires 7d;
|
| 47 |
add_header Cache-Control "public";
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
|
| 50 |
+
# SPA fallback
|
| 51 |
location / {
|
| 52 |
try_files $uri $uri/ /index.html;
|
| 53 |
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
|
| 56 |
error_page 404 /index.html;
|
|
|
|
|
|
|
| 57 |
client_max_body_size 200M;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
}
|
|
@@ -286,7 +286,7 @@ export default function PropertiesPanel() {
|
|
| 286 |
{[['Cast Shadow','castShadow'],['Receive Shadow','receiveShadow']].map(([lbl,key])=>(
|
| 287 |
<div key={key} style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>
|
| 288 |
<span style={{fontSize:11,color:'var(--text1)'}}>{lbl}</span>
|
| 289 |
-
<button onClick={()=>updateModelTransform(model.id,key,!model[key]??true)} style={{
|
| 290 |
width:36,height:20,borderRadius:10,cursor:'pointer',position:'relative',
|
| 291 |
background:(model[key]??true)?'var(--accent)':'var(--bg4)',border:'none',
|
| 292 |
transition:'background 0.2s',
|
|
|
|
| 286 |
{[['Cast Shadow','castShadow'],['Receive Shadow','receiveShadow']].map(([lbl,key])=>(
|
| 287 |
<div key={key} style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>
|
| 288 |
<span style={{fontSize:11,color:'var(--text1)'}}>{lbl}</span>
|
| 289 |
+
<button onClick={()=>updateModelTransform(model.id,key,!(model[key]??true))} style={{
|
| 290 |
width:36,height:20,borderRadius:10,cursor:'pointer',position:'relative',
|
| 291 |
background:(model[key]??true)?'var(--accent)':'var(--bg4)',border:'none',
|
| 292 |
transition:'background 0.2s',
|