robot-learning-landscape / landscape_app.py
ZifanZhao2004's picture
Deploy Robot Learning Landscape + Unified Story
c0d5651 verified
Raw
History Blame Contribute Delete
7.42 kB
"""Deployable Gradio wrapper for the Robot Learning Landscape.
The landscape is a self-contained interactive HTML page (constellation map,
Magic-Move detail panels, per-paradigm SMIL animations). It is embedded in an
<iframe srcdoc=...> so all of its JavaScript (View Transitions, click handling)
runs normally β€” inline <script> inside a plain gr.HTML does NOT execute, but an
iframe's srcdoc is a real browsing context and does.
Run (creates a public *.gradio.live share link):
.venv_robot_paradigms/bin/python landscape_app.py
Environment toggles:
LANDSCAPE_SHARE=0 # local only, no public link
LANDSCAPE_PORT=7861 # change the local port
"""
import os
import json
import gradio as gr
import gen_landscape
import gen_evolution
import gen_vlm
import gen_vlm_axes
import gen_worldmodel
import gen_reading
import gen_compare
import gen_unified_story
import gen_algorithm_lab
import gen_contribution_atlas
def build_landscape_html() -> str:
"""Render the latest landscape HTML in-memory (stays in sync with the data)."""
return gen_landscape.render()
def build_evolution_html() -> str:
"""Render the chronological evolution-timeline page in-memory."""
data = gen_evolution.build_data()
return gen_evolution.TEMPLATE.replace("__DATA_JSON__", json.dumps(data))
def build_vlm_html() -> str:
"""Render the Vision-Language-Model landscape in-memory."""
return gen_vlm.render()
def build_vlm_axes_html() -> str:
"""Render the VLM three-axes overview in-memory."""
return gen_vlm_axes.render()
def build_worldmodel_html() -> str:
"""Render the World Model landscape in-memory."""
return gen_worldmodel.render()
def build_reading_html() -> str:
"""Render the 'My Reading' directory in-memory."""
return gen_reading.render()
def build_compare_html() -> str:
"""Render the old-vs-new animation A/B compare page."""
return gen_compare.render()
def build_unified_story_html() -> str:
"""Render the animated unified-model story page in-memory."""
return gen_unified_story.render()
def build_algorithm_lab_html() -> str:
"""Render the per-paradigm synchronized algorithm explainer."""
return gen_algorithm_lab.render()
def build_contribution_atlas_html() -> str:
"""Render the contribution-focused evolution atlas."""
return gen_contribution_atlas.render()
def _esc(html: str) -> str:
# srcdoc lives inside a double-quoted attribute: escape & first, then ".
return html.replace("&", "&amp;").replace('"', "&quot;")
def combined_html() -> str:
"""Wrap the landscape + timeline as two nested iframes with a top toggle bar.
Each page keeps its own (colliding) CSS/JS because it lives in its own nested
iframe; the wrapper just shows/hides them. srcdoc is escaped once here and a
second time by iframe_embed (double-decoded by the browser, layer by layer)."""
inner_l = _esc(build_landscape_html())
inner_t = _esc(build_evolution_html())
inner_v = _esc(build_vlm_html())
inner_x = _esc(build_vlm_axes_html())
inner_w = _esc(build_worldmodel_html())
inner_r = _esc(build_reading_html())
inner_c = _esc(build_compare_html())
inner_u = _esc(build_unified_story_html())
inner_a = _esc(build_algorithm_lab_html())
inner_d = _esc(build_contribution_atlas_html())
wrapper = (
'<!doctype html><html><head><meta charset="utf-8"/>'
'<style>body{margin:0;background:#070b16;font-family:Inter,-apple-system,sans-serif;}'
'.bar{height:44px;display:flex;align-items:center;justify-content:center;gap:6px;'
'background:#0a0f1e;border-bottom:1px solid #16203a;overflow-x:auto;white-space:nowrap;}'
'.bar button{cursor:pointer;border:none;border-radius:999px;padding:7px 15px;font-size:13px;'
'flex:0 0 auto;'
'font-weight:700;color:#cbd5e1;background:transparent;font-family:inherit;}'
'.bar button.active{background:linear-gradient(90deg,#7c3aed,#0ea5e9);color:#fff;}'
'iframe{border:none;width:100%;height:calc(100vh - 44px);display:block;}'
'.hidden{display:none;}</style></head><body>'
'<div class="bar">'
'<button id="b0" class="active" onclick="show(0)">🎬 Unified Story</button>'
'<button id="b1" onclick="show(1)">πŸ§ͺ Algorithm Lab</button>'
'<button id="b2" onclick="show(2)">🧬 Contribution Atlas</button>'
'<button id="b3" onclick="show(3)">🌌 Robot Landscape</button>'
'<button id="b4" onclick="show(4)">⏳ Robot Timeline</button>'
'<button id="b5" onclick="show(5)">πŸ‘οΈ VLM Landscape</button>'
'<button id="b6" onclick="show(6)">🧭 VLM Axes</button>'
'<button id="b7" onclick="show(7)">🌍 World Models</button>'
'<button id="b8" onclick="show(8)">πŸ“š My Reading</button>'
'<button id="b9" onclick="show(9)">πŸ†š Compare</button></div>'
'<iframe id="f0" srcdoc="__U__"></iframe>'
'<iframe id="f1" class="hidden" srcdoc="__A__"></iframe>'
'<iframe id="f2" class="hidden" srcdoc="__D__"></iframe>'
'<iframe id="f3" class="hidden" srcdoc="__L__"></iframe>'
'<iframe id="f4" class="hidden" srcdoc="__T__"></iframe>'
'<iframe id="f5" class="hidden" srcdoc="__V__"></iframe>'
'<iframe id="f6" class="hidden" srcdoc="__X__"></iframe>'
'<iframe id="f7" class="hidden" srcdoc="__W__"></iframe>'
'<iframe id="f8" class="hidden" srcdoc="__R__"></iframe>'
'<iframe id="f9" class="hidden" srcdoc="__C__"></iframe>'
'<script>function show(n){for(var i=0;i<10;i++){'
'document.getElementById("f"+i).classList.toggle("hidden",i!==n);'
'document.getElementById("b"+i).classList.toggle("active",i===n);}}'
'window.addEventListener("message",function(e){var d=e.data||{};'
'if(d.type==="openAlgorithmLab"){show(1);'
'var f=document.getElementById("f1");'
'try{f.contentWindow.location.hash=d.id||"";}catch(_){}}});</script>'
'</body></html>'
)
return (wrapper.replace("__U__", inner_u).replace("__A__", inner_a).replace("__D__", inner_d)
.replace("__L__", inner_l).replace("__T__", inner_t)
.replace("__V__", inner_v).replace("__X__", inner_x).replace("__W__", inner_w)
.replace("__R__", inner_r).replace("__C__", inner_c))
def iframe_embed(html: str) -> str:
srcdoc = _esc(html)
return (
f'<iframe srcdoc="{srcdoc}" '
'title="Robot Learning Landscape" '
'allow="fullscreen; clipboard-write" '
'style="width:100%;height:92vh;min-height:700px;border:none;'
'border-radius:14px;background:#070b16;display:block;"></iframe>'
)
with gr.Blocks(title="Robot Learning Landscape") as demo:
gr.HTML(iframe_embed(combined_html()), padding=False, container=False, min_height=700)
if __name__ == "__main__":
share = os.environ.get("LANDSCAPE_SHARE", "1") == "1"
port = int(os.environ.get("LANDSCAPE_PORT", "7861"))
demo.launch(
server_name="0.0.0.0", # also reachable on your LAN
server_port=port,
share=share, # public *.gradio.live link when True
inbrowser=False,
theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="violet"),
css=".gradio-container{max-width:100% !important; padding:6px !important;}"
" footer{display:none !important;}",
)