BolyosCsaba commited on
Commit
4f032fd
ยท
1 Parent(s): 573b7f6

fix: inline Three.js + characters.js into srcdoc iframe

Browse files

Instead 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

Files changed (2) hide show
  1. app.py +8 -8
  2. studio.py +49 -11
app.py CHANGED
@@ -87,15 +87,15 @@ BRIDGE_JS = """
87
 
88
 
89
  def _wrap_in_iframe(studio_html: str) -> str:
90
- """Write studio HTML to a file and return an iframe loading it via src."""
91
- import hashlib
92
- key = hashlib.md5(studio_html[:200].encode()).hexdigest()[:8]
93
- filename = f"sandbox_cache/studio_{key}.html"
94
- Path(filename).write_text(studio_html)
95
  return (
96
- f'<iframe id="ivds-studio-iframe" src="/gradio_api/file={filename}" '
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, allowed_paths=["sandbox_cache"])
 
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
- _CHARACTERS_JS_URL = "/gradio_api/file=sandbox_cache/characters.js"
7
- _THREEJS_CDN = "https://unpkg.com/three@0.167.0/build/three.min.js"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 src="{_THREEJS_CDN}"></script>
69
- <script src="{_CHARACTERS_JS_URL}"></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 = [