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
app.py
CHANGED
|
@@ -48,10 +48,11 @@ MOBILE_CHECK_JS = """
|
|
| 48 |
</script>
|
| 49 |
"""
|
| 50 |
|
| 51 |
-
# โโ Streaming bridge JS โ
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
# โโ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 160 |
-
|
| 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();
|