BolyosCsaba commited on
Commit
da5077d
ยท
1 Parent(s): 5380bcc

fix: render studio in iframe srcdoc to isolate styles, pre-render on load

Browse files

- Wrap studio HTML in <iframe srcdoc> to prevent CSS leaking into Gradio page
- Bridge uses postMessage to forward data into iframe
- Studio pre-renders immediately with default 2 selected models
- Studio scene visible on page load, no need to click Generate first

Files changed (2) hide show
  1. app.py +35 -16
  2. studio.py +7 -0
app.py CHANGED
@@ -48,10 +48,11 @@ MOBILE_CHECK_JS = """
48
  </script>
49
  """
50
 
51
- # โ”€โ”€ Streaming bridge JS โ€” injects once into the page โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
52
  BRIDGE_JS = """
53
  <script>
54
  (function() {
 
55
  function attachBridge() {
56
  var container = document.getElementById('studio-data');
57
  if (!container) { setTimeout(attachBridge, 300); return; }
@@ -61,10 +62,14 @@ BRIDGE_JS = """
61
 
62
  function onUpdate() {
63
  var val = el.value !== undefined ? el.value : (el.textContent || '');
64
- if (!val) return;
 
65
  try {
66
  var msg = JSON.parse(val);
67
- if (window.studioUpdate) window.studioUpdate(msg);
 
 
 
68
  } catch(_) {}
69
  }
70
 
@@ -80,6 +85,17 @@ BRIDGE_JS = """
80
  </script>
81
  """
82
 
 
 
 
 
 
 
 
 
 
 
 
83
  # โ”€โ”€ Background temp-dir cleanup daemon โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
84
  def _cleanup_loop() -> None:
85
  import glob
@@ -148,7 +164,17 @@ def cancel_pipeline(request: gr.Request | None = None):
148
  flag.set()
149
 
150
 
151
- # โ”€โ”€ Start generation: build + inject studio HTML first โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
 
 
 
 
 
 
 
 
 
152
  def start_generation(
153
  trigger: str,
154
  selected_models: list[str],
@@ -156,11 +182,8 @@ def start_generation(
156
  ):
157
  if not oauth_token or not oauth_token.token:
158
  return gr.update(), gr.update(visible=False)
159
- import threading as _t
160
- pipeline_tmp = Pipeline(trigger.strip(), selected_models, oauth_token.token, _t.Event())
161
- assignments = pipeline_tmp.get_model_assignments()
162
- html = build_studio_html(assignments)
163
- return html, gr.update(visible=False)
164
 
165
 
166
  # โ”€โ”€ Generate button enable/disable โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -235,15 +258,11 @@ with gr.Blocks(title="๐ŸŽฎ Immersive Vibe Development Studio") as demo:
235
 
236
  download_file = gr.File(label="โฌ‡ Download Game ZIP", visible=False)
237
 
238
- # Right: studio scene
239
  with gr.Column(scale=6):
 
240
  studio_html = gr.HTML(
241
- value=(
242
- "<div style='color:#666;padding:3rem;font-family:monospace;"
243
- "text-align:center;background:#111;min-height:400px;"
244
- "display:flex;align-items:center;justify-content:center'>"
245
- "<span>Log in, enter a trigger, select models, then click Generate.</span></div>"
246
- ),
247
  elem_id="ivds-studio",
248
  )
249
 
 
48
  </script>
49
  """
50
 
51
+ # โ”€โ”€ Streaming bridge JS โ€” forwards data_out changes into studio iframe โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
52
  BRIDGE_JS = """
53
  <script>
54
  (function() {
55
+ var lastVal = '';
56
  function attachBridge() {
57
  var container = document.getElementById('studio-data');
58
  if (!container) { setTimeout(attachBridge, 300); return; }
 
62
 
63
  function onUpdate() {
64
  var val = el.value !== undefined ? el.value : (el.textContent || '');
65
+ if (!val || val === lastVal) return;
66
+ lastVal = val;
67
  try {
68
  var msg = JSON.parse(val);
69
+ var iframe = document.getElementById('ivds-studio-iframe');
70
+ if (iframe && iframe.contentWindow) {
71
+ iframe.contentWindow.postMessage(msg, '*');
72
+ }
73
  } catch(_) {}
74
  }
75
 
 
85
  </script>
86
  """
87
 
88
+
89
+ def _wrap_in_iframe(studio_html: str) -> str:
90
+ """Wrap raw studio HTML in a sandboxed iframe via srcdoc."""
91
+ import html as html_mod
92
+ escaped = html_mod.escape(studio_html)
93
+ return (
94
+ f'<iframe id="ivds-studio-iframe" srcdoc="{escaped}" '
95
+ f'style="width:100%;height:600px;border:none;border-radius:8px;background:#1a1a2e" '
96
+ f'allow="autoplay" sandbox="allow-scripts allow-same-origin"></iframe>'
97
+ )
98
+
99
  # โ”€โ”€ Background temp-dir cleanup daemon โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
100
  def _cleanup_loop() -> None:
101
  import glob
 
164
  flag.set()
165
 
166
 
167
+ # โ”€โ”€ Build studio iframe from model list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
168
+ def _build_studio_iframe(selected_models: list[str]) -> str:
169
+ """Build the studio iframe HTML for the given model list."""
170
+ import threading as _t
171
+ pipeline_tmp = Pipeline("preview demo", selected_models, "placeholder", _t.Event())
172
+ assignments = pipeline_tmp.get_model_assignments()
173
+ raw_html = build_studio_html(assignments)
174
+ return _wrap_in_iframe(raw_html)
175
+
176
+
177
+ # โ”€โ”€ Start generation: rebuild studio with actual models โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
178
  def start_generation(
179
  trigger: str,
180
  selected_models: list[str],
 
182
  ):
183
  if not oauth_token or not oauth_token.token:
184
  return gr.update(), gr.update(visible=False)
185
+ iframe = _build_studio_iframe(selected_models)
186
+ return iframe, gr.update(visible=False)
 
 
 
187
 
188
 
189
  # โ”€โ”€ Generate button enable/disable โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
258
 
259
  download_file = gr.File(label="โฌ‡ Download Game ZIP", visible=False)
260
 
261
+ # Right: studio scene (pre-rendered with default models)
262
  with gr.Column(scale=6):
263
+ _default_iframe = _build_studio_iframe(AVAILABLE_MODELS[:2])
264
  studio_html = gr.HTML(
265
+ value=_default_iframe,
 
 
 
 
 
266
  elem_id="ivds-studio",
267
  )
268
 
studio.py CHANGED
@@ -441,6 +441,13 @@ window.studioUpdate = function(msg) {{
441
  }}
442
  }};
443
 
 
 
 
 
 
 
 
444
  // Animation loop
445
  const clock = new THREE.Clock();
446
  const tmpVec = new THREE.Vector3();
 
441
  }}
442
  }};
443
 
444
+ // Listen for messages from parent (Gradio bridge)
445
+ window.addEventListener('message', function(e) {{
446
+ if (e.data && e.data.type) {{
447
+ window.studioUpdate(e.data);
448
+ }}
449
+ }});
450
+
451
  // Animation loop
452
  const clock = new THREE.Clock();
453
  const tmpVec = new THREE.Vector3();