BolyosCsaba commited on
Commit ยท
4f032fd
1
Parent(s): 573b7f6
fix: inline Three.js + characters.js into srcdoc iframe
Browse filesInstead of external script src (blocked by CSP) or file serving (blocked by
allowed_paths), fetch Three.js once at startup and inline both scripts
directly into the HTML. The srcdoc iframe becomes fully self-contained.
- Tries cdnjs (r167), unpkg, jsdelivr in order
- Falls back to local sandbox_cache/three.min.js if present
- characters.js read from local file (already in repo)
- No file serving, no CDN in iframe, no CSP issues
app.py
CHANGED
|
@@ -87,15 +87,15 @@ BRIDGE_JS = """
|
|
| 87 |
|
| 88 |
|
| 89 |
def _wrap_in_iframe(studio_html: str) -> str:
|
| 90 |
-
"""
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
return (
|
| 96 |
-
f'<iframe id="ivds-studio-iframe"
|
| 97 |
f'style="width:100%;height:600px;border:none;border-radius:8px;background:#1a1a2e" '
|
| 98 |
-
f'allow="autoplay"></iframe>'
|
| 99 |
)
|
| 100 |
|
| 101 |
# โโ Background temp-dir cleanup daemon โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -309,4 +309,4 @@ with gr.Blocks(title="๐ฎ Immersive Vibe Development Studio") as demo:
|
|
| 309 |
|
| 310 |
|
| 311 |
if __name__ == "__main__":
|
| 312 |
-
demo.launch(css=CSS, ssr_mode=False
|
|
|
|
| 87 |
|
| 88 |
|
| 89 |
def _wrap_in_iframe(studio_html: str) -> str:
|
| 90 |
+
"""Wrap self-contained studio HTML in a srcdoc iframe.
|
| 91 |
+
Three.js and characters.js are inlined, so no external requests needed.
|
| 92 |
+
"""
|
| 93 |
+
import html as html_mod
|
| 94 |
+
escaped = html_mod.escape(studio_html, quote=True)
|
| 95 |
return (
|
| 96 |
+
f'<iframe id="ivds-studio-iframe" srcdoc="{escaped}" '
|
| 97 |
f'style="width:100%;height:600px;border:none;border-radius:8px;background:#1a1a2e" '
|
| 98 |
+
f'allow="autoplay" sandbox="allow-scripts"></iframe>'
|
| 99 |
)
|
| 100 |
|
| 101 |
# โโ Background temp-dir cleanup daemon โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
| 309 |
|
| 310 |
|
| 311 |
if __name__ == "__main__":
|
| 312 |
+
demo.launch(css=CSS, ssr_mode=False)
|
studio.py
CHANGED
|
@@ -1,10 +1,54 @@
|
|
| 1 |
"""
|
| 2 |
Self-contained Three.js r167 isometric game-dev studio scene for Gradio.
|
|
|
|
|
|
|
| 3 |
"""
|
| 4 |
import json
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
def build_studio_html(model_assignments: list[dict]) -> str:
|
|
@@ -65,15 +109,9 @@ body {{ background: #1a1a2e; overflow: hidden; font-family: 'Courier New', monos
|
|
| 65 |
<div id="phase-progress"><div id="phase-fill" style="width:0%"></div></div>
|
| 66 |
</div>
|
| 67 |
|
| 68 |
-
<script
|
| 69 |
-
<script
|
| 70 |
<script>
|
| 71 |
-
if (typeof THREE === 'undefined') {{
|
| 72 |
-
document.body.innerHTML = '<div style="color:#f66;padding:2rem;font-family:monospace">'
|
| 73 |
-
+ 'ERROR: Three.js failed to load from CDN.<br>'
|
| 74 |
-
+ 'URL: {_THREEJS_CDN}</div>';
|
| 75 |
-
throw new Error('THREE is undefined');
|
| 76 |
-
}}
|
| 77 |
const assignments = {assignments_json};
|
| 78 |
|
| 79 |
const DESK_POSITIONS = [
|
|
|
|
| 1 |
"""
|
| 2 |
Self-contained Three.js r167 isometric game-dev studio scene for Gradio.
|
| 3 |
+
Three.js and characters.js are inlined at module load so the HTML is fully
|
| 4 |
+
self-contained and works in srcdoc iframes without any external requests.
|
| 5 |
"""
|
| 6 |
import json
|
| 7 |
+
import urllib.request
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def _fetch_threejs() -> str:
|
| 12 |
+
# Try local file first (committed to repo)
|
| 13 |
+
local = Path(__file__).parent / "sandbox_cache" / "three.min.js"
|
| 14 |
+
if local.exists() and local.stat().st_size > 100_000:
|
| 15 |
+
print("[studio] Using local three.min.js")
|
| 16 |
+
return local.read_text()
|
| 17 |
+
# Try CDNs
|
| 18 |
+
cdns = [
|
| 19 |
+
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r167/three.min.js",
|
| 20 |
+
"https://unpkg.com/three@0.167.0/build/three.min.js",
|
| 21 |
+
"https://cdn.jsdelivr.net/npm/three@0.167.0/build/three.min.js",
|
| 22 |
+
]
|
| 23 |
+
for url in cdns:
|
| 24 |
+
try:
|
| 25 |
+
with urllib.request.urlopen(url, timeout=15) as r:
|
| 26 |
+
data = r.read().decode("utf-8")
|
| 27 |
+
if len(data) > 100_000:
|
| 28 |
+
# Cache locally for next startup
|
| 29 |
+
try:
|
| 30 |
+
local.write_text(data)
|
| 31 |
+
except Exception:
|
| 32 |
+
pass
|
| 33 |
+
print(f"[studio] Fetched Three.js from {url} ({len(data):,} bytes)")
|
| 34 |
+
return data
|
| 35 |
+
except Exception as e:
|
| 36 |
+
print(f"[studio] CDN {url} failed: {e}")
|
| 37 |
+
print("[studio] ERROR: All Three.js sources failed")
|
| 38 |
+
return "console.error('[IVDS] Three.js failed to load โ studio will not render');"
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _load_characters_js() -> str:
|
| 42 |
+
p = Path(__file__).parent / "sandbox_cache" / "characters.js"
|
| 43 |
+
if p.exists():
|
| 44 |
+
return p.read_text()
|
| 45 |
+
return "/* characters.js not found */"
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
print("[studio] Loading Three.js from CDN...")
|
| 49 |
+
_THREEJS_JS = _fetch_threejs()
|
| 50 |
+
_CHARACTERS_JS = _load_characters_js()
|
| 51 |
+
print(f"[studio] Three.js: {len(_THREEJS_JS):,} bytes | characters.js: {len(_CHARACTERS_JS):,} bytes")
|
| 52 |
|
| 53 |
|
| 54 |
def build_studio_html(model_assignments: list[dict]) -> str:
|
|
|
|
| 109 |
<div id="phase-progress"><div id="phase-fill" style="width:0%"></div></div>
|
| 110 |
</div>
|
| 111 |
|
| 112 |
+
<script>{_THREEJS_JS}</script>
|
| 113 |
+
<script>{_CHARACTERS_JS}</script>
|
| 114 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
const assignments = {assignments_json};
|
| 116 |
|
| 117 |
const DESK_POSITIONS = [
|